Files
windmill/frontend/src/lib/validators/workspaceSettings.ts
T
Guilhem dd421845ba fix(frontend): reorganize workspace settings (#7788)
* Add vertical nav bar to workspace settings

* harmonize settings content titles

* remove sidebar icons

* add background to sidebar

* nit user section

* EEonly display

* Workspace settings general design

* Add schema validation and dirty detection

* Put critical alerts in a separated tab

* separate error success handler

* only enable save when there is some changes

* Fix dirty detection for deployment UI

* Only enable save button when changes for datatables ws storage

* Add setting footer component

* Use new footer setting for saving configs

* nit

* apply setting footer

* improve save button

* nit

* nit

* nit

* make ws app use same pattern as other tabs

* Separate scrolling between sidebar and content

* Gather error handlers

* use universal save button for object storage

* Title sentence case

* nit

* nit

* improve dirty config logic

* nit

* nit

* clean dead code

* Use settings footer for deployment settings

* Git sync settings

* move tabs

* fix dirty stats of error handlers

* nit

* nit
2026-02-09 18:22:22 +00:00

100 lines
2.5 KiB
TypeScript

import { z } from 'zod'
// Deploy path filter validation
const deployPathFilterSchema = z
.string()
.min(1, 'Path filter cannot be empty')
.refine(
(val) => {
// Allow alphanumeric characters, underscores, slashes, asterisks, and hyphens
// But don't allow consecutive slashes or ending with slash
const validChars = /^[a-zA-Z0-9/_*-]+$/
const noConsecutiveSlashes = !/\/\//.test(val)
const noEndingSlash = !val.endsWith('/')
const noEndingDash = !val.endsWith('-')
return validChars.test(val) && noConsecutiveSlashes && noEndingSlash && noEndingDash
},
{
message:
'Path filter contains invalid characters or format. Allowed: letters, numbers, /, _, -, *. Cannot end with / or -'
}
)
.refine(
(val) => {
// Validate glob patterns - * and ** are allowed, but *** is not
const invalidGlobPattern = /\*{3,}/.test(val)
return !invalidGlobPattern
},
{
message:
'Invalid glob pattern. Use * for single level wildcard or ** for multi-level wildcard'
}
)
// Webhook URL validation
const webhookUrlSchema = z
.string()
.refine(
(val) => {
if (!val || val.trim() === '') return true // Allow empty
try {
new URL(val)
return true
} catch {
return false
}
},
{
message: 'Please enter a valid URL (e.g., https://example.com/webhook)'
}
)
.optional()
.or(z.literal(''))
// Workspace encryption key validation
const encryptionKeySchema = z.string().regex(/^[a-zA-Z0-9]{64}$/, {
message: 'Encryption key must be exactly 64 characters long and contain only letters and numbers'
})
export function validateDeployPathFilters(paths: string[]): {
isValid: boolean
errors: Record<number, string>
} {
const errors: Record<number, string> = {}
paths.forEach((path, index) => {
const result = deployPathFilterSchema.safeParse(path)
if (!result.success) {
errors[index] = result.error.issues[0]?.message || 'Invalid path filter'
}
})
return {
isValid: Object.keys(errors).length === 0,
errors
}
}
export function validateWebhookUrl(url: string): {
isValid: boolean
error?: string
} {
const result = webhookUrlSchema.safeParse(url)
return {
isValid: result.success,
error: result.success ? undefined : result.error.issues[0]?.message || 'Invalid URL'
}
}
export function validateEncryptionKey(key: string): {
isValid: boolean
error?: string
} {
const result = encryptionKeySchema.safeParse(key)
return {
isValid: result.success,
error: result.success ? undefined : result.error.issues[0]?.message || 'Invalid encryption key'
}
}