From 02b96ba00f136d88297c5fcd1d4098442a2c7a5e Mon Sep 17 00:00:00 2001 From: Alexander Petric Date: Wed, 8 Jan 2025 12:50:08 -0500 Subject: [PATCH 1/5] doc link job labels (#5028) --- frontend/src/lib/components/runs/RunsFilter.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/components/runs/RunsFilter.svelte b/frontend/src/lib/components/runs/RunsFilter.svelte index ef909b22633ed..f1e866c2e3900 100644 --- a/frontend/src/lib/components/runs/RunsFilter.svelte +++ b/frontend/src/lib/components/runs/RunsFilter.svelte @@ -271,8 +271,8 @@ Label Labels are string values in the array at the result field 'wm_labels' to easily - filter themJob Labels are string values in the array at the result field 'wm_labels' to easily + filter them. From 4c8c90e7fbde8ae3c58ff7d709a05d2ac943e2ad Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 8 Jan 2025 23:01:47 +0100 Subject: [PATCH 2/5] nits --- frontend/package.json | 2 +- .../triggers/RouteEditorConfigSection.svelte | 4 ++-- .../components/triggers/WebhooksConfigSection.svelte | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index dd4a8d862faad..82a304771e702 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.444.0", + "version": "1.444.2", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/frontend/src/lib/components/triggers/RouteEditorConfigSection.svelte b/frontend/src/lib/components/triggers/RouteEditorConfigSection.svelte index 1ebc2da408879..7526dac0439f3 100644 --- a/frontend/src/lib/components/triggers/RouteEditorConfigSection.svelte +++ b/frontend/src/lib/components/triggers/RouteEditorConfigSection.svelte @@ -9,7 +9,7 @@ import CopyableCodeBlock from '$lib/components/details/CopyableCodeBlock.svelte' import { bash } from 'svelte-highlight/languages' import { HttpTriggerService } from '$lib/gen' - import { page } from '$app/stores' + // import { page } from '$app/stores' import { isCloudHosted } from '$lib/cloud' import { base } from '$lib/base' import type { CaptureInfo } from './CaptureSection.svelte' @@ -59,7 +59,7 @@ } function getHttpRoute(route_path: string | undefined) { - return `${$page.url.origin}${base}/api/r/${ + return `${location.origin}${base}/api/r/${ isCloudHosted() ? $workspaceStore + '/' : '' }${route_path}` } diff --git a/frontend/src/lib/components/triggers/WebhooksConfigSection.svelte b/frontend/src/lib/components/triggers/WebhooksConfigSection.svelte index da67569709b7a..2a50f729b4c8f 100644 --- a/frontend/src/lib/components/triggers/WebhooksConfigSection.svelte +++ b/frontend/src/lib/components/triggers/WebhooksConfigSection.svelte @@ -15,7 +15,7 @@ import { typescript } from 'svelte-highlight/languages' import ClipboardPanel from '../details/ClipboardPanel.svelte' import { copyToClipboard } from '$lib/utils' - import { page } from '$app/stores' + // import { page } from '$app/stores' import { base } from '$lib/base' import TriggerTokens from './TriggerTokens.svelte' import { workspaceStore, userStore } from '$lib/stores' @@ -54,7 +54,7 @@ $: webhooks = isFlow ? computeFlowWebhooks(path) : computeScriptWebhooks(hash, path) function computeScriptWebhooks(hash: string | undefined, path: string) { - let webhookBase = `${$page.url.origin}${base}/api/w/${$workspaceStore}/jobs` + let webhookBase = `${location.origin}${base}/api/w/${$workspaceStore}/jobs` return { async: { hash: `${webhookBase}/run/h/${hash}`, @@ -69,7 +69,7 @@ } function computeFlowWebhooks(path: string) { - let webhooksBase = `${$page.url.origin}${base}/api/w/${$workspaceStore}/jobs` + let webhooksBase = `${location.origin}${base}/api/w/${$workspaceStore}/jobs` let urlAsync = `${webhooksBase}/run/f/${path}` let urlSync = `${webhooksBase}/run_wait_result/f/${path}` @@ -158,7 +158,7 @@ function waitForJobCompletion(UUID) { return new Promise(async (resolve, reject) => { try { const endpoint = \`${ - $page.url.origin + location.origin }/api/w/${$workspaceStore}/jobs_u/completed/get_result_maybe/\${UUID}\`; const checkResponse = await fetch(endpoint, { method: 'GET', @@ -186,7 +186,7 @@ function waitForJobCompletion(UUID) { return `${mainFunction}\n\n${triggerJobFunction}\n\n${waitForJobCompletionFunction}` } - let captureUrl = `${$page.url.origin}/api/w/${$workspaceStore}/capture_u/webhook/${ + let captureUrl = `${location.origin}/api/w/${$workspaceStore}/capture_u/webhook/${ isFlow ? 'flow' : 'script' }/${path}` @@ -211,7 +211,7 @@ ${ webhookType === 'sync' ? 'echo -E $RESULT | jq' : ` -URL="${$page.url.origin}/api/w/${$workspaceStore}/jobs_u/completed/get_result_maybe/$UUID" +URL="${location.origin}/api/w/${$workspaceStore}/jobs_u/completed/get_result_maybe/$UUID" while true; do curl -s -H "Authorization: Bearer $TOKEN" $URL -o res.json COMPLETED=$(cat res.json | jq .completed) From 53c62f2dbc4d43f02860fc9606a57996228be37c Mon Sep 17 00:00:00 2001 From: Alexander Petric Date: Thu, 9 Jan 2025 00:40:54 -0500 Subject: [PATCH 3/5] fix: support html in github markdown plugin (#5031) * support html in github markdown plugin * also support github alerts in markdown --- frontend/package-lock.json | 230 +++++++++++++++++- frontend/package.json | 2 + .../src/lib/components/GfmMarkdown.svelte | 96 +++++++- 3 files changed, 324 insertions(+), 4 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ae323588ca7c4..1cb3b75f390de 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -58,6 +58,8 @@ "panzoom": "^9.4.3", "pdfjs-dist": "4.8.69", "quill": "^1.3.7", + "rehype-github-alerts": "^3.0.0", + "rehype-raw": "^7.0.0", "rfc4648": "^1.5.3", "svelte-carousel": "^1.0.25", "svelte-chartjs": "^3.1.5", @@ -5870,6 +5872,16 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -6707,7 +6719,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, "engines": { "node": ">=0.12" }, @@ -7911,6 +7922,131 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.2.tgz", + "integrity": "sha512-SfMzfdAi/zAoZ1KkFEyyeXBn7u/ShQrfd675ZEE9M3qj+PMFX05xubzRyF76CCSJu8au9jgVxDV1+okFvgZU4A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^6.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", + "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.0.tgz", + "integrity": "sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/highlight.js": { "version": "11.9.0", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz", @@ -7947,6 +8083,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", @@ -10415,6 +10561,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "license": "MIT", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/pascal-case": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", @@ -11349,6 +11507,16 @@ "svelte": "^3.2.0 || ^4.0.0-next.0" } }, + "node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/protocol-buffers-schema": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", @@ -11669,6 +11837,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rehype-github-alerts": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rehype-github-alerts/-/rehype-github-alerts-3.0.0.tgz", + "integrity": "sha512-m8Lm+V0Ee0T9JqaLN0eBqtyUSfMUDFhAOIjbvoAQfI6EKtxKsIGWP6PaQE01UBHat0K7dBtnaeyCmJuUO7ylHg==", + "license": "MIT", + "dependencies": { + "hast-util-from-html": "^2.0.1", + "hast-util-is-element": "^3.0.0", + "unist-util-visit": "^5.0.0" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-gfm": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", @@ -12155,6 +12349,16 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -13484,6 +13688,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vfile-message": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", @@ -13702,6 +13920,16 @@ "node": ">=14.0.0" } }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/web-worker": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 82a304771e702..275e8c8ca8e67 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -133,6 +133,8 @@ "panzoom": "^9.4.3", "pdfjs-dist": "4.8.69", "quill": "^1.3.7", + "rehype-github-alerts": "^3.0.0", + "rehype-raw": "^7.0.0", "rfc4648": "^1.5.3", "svelte-carousel": "^1.0.25", "svelte-chartjs": "^3.1.5", diff --git a/frontend/src/lib/components/GfmMarkdown.svelte b/frontend/src/lib/components/GfmMarkdown.svelte index 59ed3871663d7..7392de5484568 100644 --- a/frontend/src/lib/components/GfmMarkdown.svelte +++ b/frontend/src/lib/components/GfmMarkdown.svelte @@ -1,12 +1,18 @@
- +
From 0c391372cca96da820e8438a3f685f9895dbff73 Mon Sep 17 00:00:00 2001 From: Alexander Petric Date: Thu, 9 Jan 2025 00:42:36 -0500 Subject: [PATCH 4/5] feat(frontend): allow workspace admin to set workspace color (#5032) * feat(frontend): allow workspace admin to set workspace color * sqlx prep * sqlx prep * make border wider, add removed comment --- ...c61296a3ff7489ae12f52a19f9543173ac597.json | 6 ++ ...12d72c90235bb40d2f66e6fb235c990d78352.json | 52 ++++++++++ ...dcc40f463cbc52d94ed9315cf9a547d4c89f2.json | 6 ++ ...409edfdc1847d0348df2ea55504ddaaa67736.json | 40 ++++++++ ...3d3e218fa224bd0bb311e7db776c7fd69cf0b.json | 15 +++ ...14cbf509b5afcc2647048c9bfc839d8f290ad.json | 15 +++ ...950295e0d0fbdabb38434534fb3430eeddc25.json | 53 ++++++++++ .../20250107212922_workspace_color.down.sql | 1 + .../20250107212922_workspace_color.up.sql | 1 + backend/windmill-api/openapi.yaml | 33 +++++++ backend/windmill-api/src/workspaces.rs | 63 ++++++++++-- .../settings/ChangeWorkspaceColor.svelte | 96 +++++++++++++++++++ .../lib/components/sidebar/MenuButton.svelte | 8 +- .../components/sidebar/WorkspaceMenu.svelte | 63 +++++++----- frontend/src/lib/stores.ts | 4 +- .../user/(user)/create_workspace/+page.svelte | 20 ++++ .../user/(user)/workspaces/+page.svelte | 13 ++- .../(logged)/workspace_settings/+page.svelte | 2 + 18 files changed, 453 insertions(+), 38 deletions(-) create mode 100644 backend/.sqlx/query-3651ed42be75d41ab0387f1551012d72c90235bb40d2f66e6fb235c990d78352.json create mode 100644 backend/.sqlx/query-6c845f168b6265e6cf92e4d33c5409edfdc1847d0348df2ea55504ddaaa67736.json create mode 100644 backend/.sqlx/query-d2668c2d4ece82a3617f098fe993d3e218fa224bd0bb311e7db776c7fd69cf0b.json create mode 100644 backend/.sqlx/query-e54eb583f011cea6e4d533f4d2014cbf509b5afcc2647048c9bfc839d8f290ad.json create mode 100644 backend/.sqlx/query-eed16e356f3f36183c3db13fcc1950295e0d0fbdabb38434534fb3430eeddc25.json create mode 100644 backend/migrations/20250107212922_workspace_color.down.sql create mode 100644 backend/migrations/20250107212922_workspace_color.up.sql create mode 100644 frontend/src/lib/components/settings/ChangeWorkspaceColor.svelte diff --git a/backend/.sqlx/query-1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597.json b/backend/.sqlx/query-1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597.json index 686158d9a1d34..64d273ed70528 100644 --- a/backend/.sqlx/query-1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597.json +++ b/backend/.sqlx/query-1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597.json @@ -122,6 +122,11 @@ "ordinal": 23, "name": "mute_critical_alerts", "type_info": "Bool" + }, + { + "ordinal": 24, + "name": "color", + "type_info": "Varchar" } ], "parameters": { @@ -153,6 +158,7 @@ false, true, true, + true, true ] }, diff --git a/backend/.sqlx/query-3651ed42be75d41ab0387f1551012d72c90235bb40d2f66e6fb235c990d78352.json b/backend/.sqlx/query-3651ed42be75d41ab0387f1551012d72c90235bb40d2f66e6fb235c990d78352.json new file mode 100644 index 0000000000000..87e705630b285 --- /dev/null +++ b/backend/.sqlx/query-3651ed42be75d41ab0387f1551012d72c90235bb40d2f66e6fb235c990d78352.json @@ -0,0 +1,52 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT workspace.id, workspace.name, workspace.owner, workspace.deleted, workspace.premium, workspace_settings.color\n FROM workspace\n LEFT JOIN workspace_settings ON workspace.id = workspace_settings.workspace_id\n JOIN usr ON usr.workspace_id = workspace.id\n WHERE usr.email = $1 AND workspace.deleted = false", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "owner", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "deleted", + "type_info": "Bool" + }, + { + "ordinal": 4, + "name": "premium", + "type_info": "Bool" + }, + { + "ordinal": 5, + "name": "color", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + true + ] + }, + "hash": "3651ed42be75d41ab0387f1551012d72c90235bb40d2f66e6fb235c990d78352" +} diff --git a/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json b/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json index 6aab7e7796a9a..6092b0551a48c 100644 --- a/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json +++ b/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json @@ -122,6 +122,11 @@ "ordinal": 23, "name": "mute_critical_alerts", "type_info": "Bool" + }, + { + "ordinal": 24, + "name": "color", + "type_info": "Varchar" } ], "parameters": { @@ -153,6 +158,7 @@ false, true, true, + true, true ] }, diff --git a/backend/.sqlx/query-6c845f168b6265e6cf92e4d33c5409edfdc1847d0348df2ea55504ddaaa67736.json b/backend/.sqlx/query-6c845f168b6265e6cf92e4d33c5409edfdc1847d0348df2ea55504ddaaa67736.json new file mode 100644 index 0000000000000..b4199d4ca1a90 --- /dev/null +++ b/backend/.sqlx/query-6c845f168b6265e6cf92e4d33c5409edfdc1847d0348df2ea55504ddaaa67736.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT workspace.id, workspace.name, usr.username, workspace_settings.color\n FROM workspace\n JOIN usr ON usr.workspace_id = workspace.id\n JOIN workspace_settings ON workspace_settings.workspace_id = workspace.id\n WHERE usr.email = $1 AND workspace.deleted = false", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "username", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "color", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + true + ] + }, + "hash": "6c845f168b6265e6cf92e4d33c5409edfdc1847d0348df2ea55504ddaaa67736" +} diff --git a/backend/.sqlx/query-d2668c2d4ece82a3617f098fe993d3e218fa224bd0bb311e7db776c7fd69cf0b.json b/backend/.sqlx/query-d2668c2d4ece82a3617f098fe993d3e218fa224bd0bb311e7db776c7fd69cf0b.json new file mode 100644 index 0000000000000..e3bebd610a9d2 --- /dev/null +++ b/backend/.sqlx/query-d2668c2d4ece82a3617f098fe993d3e218fa224bd0bb311e7db776c7fd69cf0b.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE workspace_settings SET color = $1 WHERE workspace_id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Text" + ] + }, + "nullable": [] + }, + "hash": "d2668c2d4ece82a3617f098fe993d3e218fa224bd0bb311e7db776c7fd69cf0b" +} diff --git a/backend/.sqlx/query-e54eb583f011cea6e4d533f4d2014cbf509b5afcc2647048c9bfc839d8f290ad.json b/backend/.sqlx/query-e54eb583f011cea6e4d533f4d2014cbf509b5afcc2647048c9bfc839d8f290ad.json new file mode 100644 index 0000000000000..b94339710d90b --- /dev/null +++ b/backend/.sqlx/query-e54eb583f011cea6e4d533f4d2014cbf509b5afcc2647048c9bfc839d8f290ad.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO workspace_settings\n (workspace_id, color)\n VALUES ($1, $2)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "e54eb583f011cea6e4d533f4d2014cbf509b5afcc2647048c9bfc839d8f290ad" +} diff --git a/backend/.sqlx/query-eed16e356f3f36183c3db13fcc1950295e0d0fbdabb38434534fb3430eeddc25.json b/backend/.sqlx/query-eed16e356f3f36183c3db13fcc1950295e0d0fbdabb38434534fb3430eeddc25.json new file mode 100644 index 0000000000000..5e0817ae84196 --- /dev/null +++ b/backend/.sqlx/query-eed16e356f3f36183c3db13fcc1950295e0d0fbdabb38434534fb3430eeddc25.json @@ -0,0 +1,53 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT workspace.id, workspace.name, workspace.owner, workspace.deleted, workspace.premium, workspace_settings.color\n FROM workspace\n LEFT JOIN workspace_settings ON workspace.id = workspace_settings.workspace_id\n LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "owner", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "deleted", + "type_info": "Bool" + }, + { + "ordinal": 4, + "name": "premium", + "type_info": "Bool" + }, + { + "ordinal": 5, + "name": "color", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + true + ] + }, + "hash": "eed16e356f3f36183c3db13fcc1950295e0d0fbdabb38434534fb3430eeddc25" +} diff --git a/backend/migrations/20250107212922_workspace_color.down.sql b/backend/migrations/20250107212922_workspace_color.down.sql new file mode 100644 index 0000000000000..80c87a3cd448c --- /dev/null +++ b/backend/migrations/20250107212922_workspace_color.down.sql @@ -0,0 +1 @@ +ALTER TABLE workspace_settings DROP COLUMN color; diff --git a/backend/migrations/20250107212922_workspace_color.up.sql b/backend/migrations/20250107212922_workspace_color.up.sql new file mode 100644 index 0000000000000..c98aa3d4c2ece --- /dev/null +++ b/backend/migrations/20250107212922_workspace_color.up.sql @@ -0,0 +1 @@ +ALTER TABLE workspace_settings ADD COLUMN color VARCHAR(7) DEFAULT NULL; diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index ab1a252b2fc00..4443894e5e30e 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1576,6 +1576,30 @@ paths: schema: type: string + /w/{workspace}/workspaces/change_workspace_color: + post: + summary: change workspace id + operationId: changeWorkspaceColor + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + content: + application/json: + schema: + type: object + properties: + color: + type: string + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + /w/{workspace}/users/whois/{username}: get: summary: whois @@ -1722,6 +1746,8 @@ paths: $ref: "#/components/schemas/WorkspaceDefaultScripts" mute_critical_alerts: type: boolean + color: + type: string required: - code_completion_enabled - automatic_billing @@ -12765,10 +12791,13 @@ components: type: string username: type: string + color: + type: string required: - id - name - username + - color required: - email - workspaces @@ -12782,6 +12811,8 @@ components: type: string username: type: string + color: + type: string required: - id - name @@ -12797,6 +12828,8 @@ components: type: string domain: type: string + color: + type: string required: - id - name diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index 433450b6e80e7..ea07df6171c66 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -106,6 +106,7 @@ pub fn workspaced_service() -> Router { .route("/leave", post(leave_workspace)) .route("/get_workspace_name", get(get_workspace_name)) .route("/change_workspace_name", post(change_workspace_name)) + .route("/change_workspace_color", post(change_workspace_color)) .route( "/change_workspace_id", post(crate::workspaces_extra::change_workspace_id), @@ -158,6 +159,7 @@ struct Workspace { owner: String, deleted: bool, premium: bool, + color: Option, } #[derive(FromRow, Serialize, Debug)] @@ -186,6 +188,7 @@ pub struct WorkspaceSettings { pub automatic_billing: bool, pub default_scripts: Option, pub mute_critical_alerts: Option, + pub color: Option, } #[derive(FromRow, Serialize, Debug)] @@ -263,6 +266,7 @@ struct CreateWorkspace { id: String, name: String, username: Option, + color: Option, } #[derive(Deserialize)] @@ -282,6 +286,7 @@ struct UserWorkspace { pub id: String, pub name: String, pub username: String, + pub color: Option, } #[derive(Deserialize)] @@ -376,8 +381,11 @@ async fn list_workspaces( let mut tx = user_db.begin(&authed).await?; let workspaces = sqlx::query_as!( Workspace, - "SELECT workspace.* FROM workspace, usr WHERE usr.workspace_id = workspace.id AND \ - usr.email = $1 AND deleted = false", + "SELECT workspace.id, workspace.name, workspace.owner, workspace.deleted, workspace.premium, workspace_settings.color + FROM workspace + LEFT JOIN workspace_settings ON workspace.id = workspace_settings.workspace_id + JOIN usr ON usr.workspace_id = workspace.id + WHERE usr.email = $1 AND workspace.deleted = false", authed.email ) .fetch_all(&mut *tx) @@ -1331,7 +1339,10 @@ async fn list_workspaces_as_super_admin( let mut tx = user_db.begin(&authed).await?; let workspaces = sqlx::query_as!( Workspace, - "SELECT * FROM workspace LIMIT $1 OFFSET $2", + "SELECT workspace.id, workspace.name, workspace.owner, workspace.deleted, workspace.premium, workspace_settings.color + FROM workspace + LEFT JOIN workspace_settings ON workspace.id = workspace_settings.workspace_id + LIMIT $1 OFFSET $2", per_page as i32, offset as i32 ) @@ -1348,9 +1359,11 @@ async fn user_workspaces( let mut tx = db.begin().await?; let workspaces = sqlx::query_as!( UserWorkspace, - "SELECT workspace.id, workspace.name, usr.username - FROM workspace, usr WHERE usr.workspace_id = workspace.id AND usr.email = $1 AND deleted = \ - false", + "SELECT workspace.id, workspace.name, usr.username, workspace_settings.color + FROM workspace + JOIN usr ON usr.workspace_id = workspace.id + JOIN workspace_settings ON workspace_settings.workspace_id = workspace.id + WHERE usr.email = $1 AND workspace.deleted = false", email ) .fetch_all(&mut *tx) @@ -1430,9 +1443,10 @@ async fn create_workspace( .await?; sqlx::query!( "INSERT INTO workspace_settings - (workspace_id) - VALUES ($1)", - nw.id + (workspace_id, color) + VALUES ($1, $2)", + nw.id, + nw.color, ) .execute(&mut *tx) .await?; @@ -1943,6 +1957,11 @@ struct ChangeWorkspaceName { new_name: String, } +#[derive(Deserialize)] +struct ChangeWorkspaceColor { + color: Option, +} + async fn change_workspace_name( authed: ApiAuthed, Path(w_id): Path, @@ -1977,6 +1996,32 @@ async fn change_workspace_name( Ok(format!("updated workspace name to {}", &rw.new_name)) } +async fn change_workspace_color( + authed: ApiAuthed, + Path(w_id): Path, + Extension(db): Extension, + Json(rw): Json, +) -> Result { + require_admin(authed.is_admin, &authed.username)?; + + let mut tx = db.begin().await?; + + sqlx::query!( + "UPDATE workspace_settings SET color = $1 WHERE workspace_id = $2", + rw.color, + &w_id + ) + .execute(&mut *tx) + .await?; + + tx.commit().await?; + + Ok(format!( + "updated workspace color to {}", + rw.color.as_deref().unwrap_or("no color") + )) +} + async fn get_usage(Extension(db): Extension, Path(w_id): Path) -> Result { let usage = sqlx::query_scalar!( " diff --git a/frontend/src/lib/components/settings/ChangeWorkspaceColor.svelte b/frontend/src/lib/components/settings/ChangeWorkspaceColor.svelte new file mode 100644 index 0000000000000..dd325f1354d41 --- /dev/null +++ b/frontend/src/lib/components/settings/ChangeWorkspaceColor.svelte @@ -0,0 +1,96 @@ + + +
+

Workspace Color

+
+ {#if workspaceColor} +
+ {:else} + No color set + {/if} +
+
+ + +
+ +
+ + + + +
diff --git a/frontend/src/lib/components/sidebar/MenuButton.svelte b/frontend/src/lib/components/sidebar/MenuButton.svelte index 5b3c048d0e5bc..ab7a32274fa4c 100644 --- a/frontend/src/lib/components/sidebar/MenuButton.svelte +++ b/frontend/src/lib/components/sidebar/MenuButton.svelte @@ -11,7 +11,7 @@ export let stopPropagationOnClick: boolean = false export let shortcut: string = '' export let notificationsCount: number = 0 - + export let color: string | null = null let dispatch = createEventDispatcher() @@ -26,10 +26,12 @@ 'group flex items-center px-2 py-2 font-light rounded-md h-8 gap-3 w-full', lightMode ? 'text-primary hover:bg-surface-hover ' - : ' hover:bg-[#2A3648] text-primary-inverse dark:text-primary', + : 'hover:bg-[#2A3648] text-primary-inverse dark:text-primary', + color ? 'border-4' : '', 'transition-all', $$props.class )} + style={color ? `border-color: ${color}; padding: 0 calc(0.5rem - 4px);` : ''} title={label} > {#if icon} @@ -50,7 +52,7 @@
- + w.id === $workspaceStore)?.color} + />
@@ -63,17 +70,29 @@ {#each $userWorkspaces as workspace} @@ -93,19 +112,19 @@
{/if} {#if !strictWorkspaceSelect} - + {/if} {#if ($userStore?.is_admin || $superadmin) && !strictWorkspaceSelect}
diff --git a/frontend/src/lib/stores.ts b/frontend/src/lib/stores.ts index 8af1ddf5ac267..65ba9f44ef79d 100644 --- a/frontend/src/lib/stores.ts +++ b/frontend/src/lib/stores.ts @@ -55,6 +55,7 @@ export const userWorkspaces: Readable< id: string name: string username: string + color: string | null }> > = derived([usersWorkspaceStore, superadmin], ([store, superadmin]) => { const originalWorkspaces = store?.workspaces ?? [] @@ -64,7 +65,8 @@ export const userWorkspaces: Readable< { id: 'admins', name: 'Admins', - username: 'superadmin' + username: 'superadmin', + color: null } ] } else { diff --git a/frontend/src/routes/(root)/(logged)/user/(user)/create_workspace/+page.svelte b/frontend/src/routes/(root)/(logged)/user/(user)/create_workspace/+page.svelte index fd47e0f0113a3..b57b5d2fb8cbd 100644 --- a/frontend/src/routes/(root)/(logged)/user/(user)/create_workspace/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/user/(user)/create_workspace/+page.svelte @@ -37,10 +37,19 @@ let codeCompletionEnabled = true let checking = false + let workspaceColor: string | null = null + let colorEnabled = false + + function generateRandomColor() { + const randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0'); + workspaceColor = randomColor; + } + $: id = name.toLowerCase().replace(/\s/gi, '-') $: validateName(id) $: errorUser = validateUsername(username) + $: colorEnabled && !workspaceColor && generateRandomColor() async function validateName(id: string): Promise { checking = true @@ -60,6 +69,7 @@ requestBody: { id, name, + color: colorEnabled && workspaceColor ? workspaceColor : undefined, username: automateUsernameCreation ? undefined : username } }) @@ -186,6 +196,16 @@ {/if} + {#if !automateUsernameCreation}