mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-18 00:03:11 +00:00
* 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
100 lines
2.5 KiB
TypeScript
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'
|
|
}
|
|
}
|