mirror of https://github.com/langgenius/dify.git
201 lines
6.7 KiB
TypeScript
201 lines
6.7 KiB
TypeScript
import { BlockEnum } from '../../types'
|
|
import type { NodeDefault } from '../../types'
|
|
import type { ScheduleTriggerNodeType } from './types'
|
|
import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
|
|
import { isValidCronExpression } from './utils/cron-parser'
|
|
import { getNextExecutionTimes } from './utils/execution-time-calculator'
|
|
const isValidTimeFormat = (time: string): boolean => {
|
|
const timeRegex = /^(0?\d|1[0-2]):[0-5]\d (AM|PM)$/
|
|
if (!timeRegex.test(time)) return false
|
|
|
|
const [timePart, period] = time.split(' ')
|
|
const [hour, minute] = timePart.split(':')
|
|
const hourNum = Number.parseInt(hour, 10)
|
|
const minuteNum = Number.parseInt(minute, 10)
|
|
|
|
return hourNum >= 1 && hourNum <= 12
|
|
&& minuteNum >= 0 && minuteNum <= 59
|
|
&& ['AM', 'PM'].includes(period)
|
|
}
|
|
|
|
const validateHourlyConfig = (config: any, t: any): string => {
|
|
const i18nPrefix = 'workflow.errorMsg'
|
|
|
|
if (!config.datetime)
|
|
return t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.triggerSchedule.startTime') })
|
|
|
|
const startTime = new Date(config.datetime)
|
|
if (Number.isNaN(startTime.getTime()))
|
|
return t('workflow.nodes.triggerSchedule.invalidStartTime')
|
|
|
|
if (startTime <= new Date())
|
|
return t('workflow.nodes.triggerSchedule.startTimeMustBeFuture')
|
|
|
|
const recurEvery = config.recur_every || 1
|
|
if (recurEvery < 1 || recurEvery > 999)
|
|
return t('workflow.nodes.triggerSchedule.invalidRecurEvery')
|
|
|
|
if (!config.recur_unit || !['hours', 'minutes'].includes(config.recur_unit))
|
|
return t('workflow.nodes.triggerSchedule.invalidRecurUnit')
|
|
|
|
return ''
|
|
}
|
|
|
|
const validateDailyConfig = (config: any, t: any): string => {
|
|
const i18nPrefix = 'workflow.errorMsg'
|
|
|
|
if (!config.time)
|
|
return t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.triggerSchedule.time') })
|
|
|
|
if (!isValidTimeFormat(config.time))
|
|
return t('workflow.nodes.triggerSchedule.invalidTimeFormat')
|
|
|
|
return ''
|
|
}
|
|
|
|
const validateWeeklyConfig = (config: any, t: any): string => {
|
|
const dailyError = validateDailyConfig(config, t)
|
|
if (dailyError) return dailyError
|
|
|
|
const i18nPrefix = 'workflow.errorMsg'
|
|
|
|
if (!config.weekdays || config.weekdays.length === 0)
|
|
return t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.triggerSchedule.weekdays') })
|
|
|
|
const validWeekdays = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
|
|
for (const day of config.weekdays) {
|
|
if (!validWeekdays.includes(day))
|
|
return t('workflow.nodes.triggerSchedule.invalidWeekday', { weekday: day })
|
|
}
|
|
|
|
return ''
|
|
}
|
|
|
|
const validateMonthlyConfig = (config: any, t: any): string => {
|
|
const dailyError = validateDailyConfig(config, t)
|
|
if (dailyError) return dailyError
|
|
|
|
const i18nPrefix = 'workflow.errorMsg'
|
|
|
|
if (!config.monthly_day)
|
|
return t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.triggerSchedule.monthlyDay') })
|
|
|
|
if (config.monthly_day !== 'last'
|
|
&& (typeof config.monthly_day !== 'number'
|
|
|| config.monthly_day < 1
|
|
|| config.monthly_day > 31))
|
|
return t('workflow.nodes.triggerSchedule.invalidMonthlyDay')
|
|
|
|
return ''
|
|
}
|
|
|
|
const validateOnceConfig = (config: any, t: any): string => {
|
|
const i18nPrefix = 'workflow.errorMsg'
|
|
|
|
if (!config.datetime)
|
|
return t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.triggerSchedule.executionTime') })
|
|
|
|
const executionTime = new Date(config.datetime)
|
|
if (Number.isNaN(executionTime.getTime()))
|
|
return t('workflow.nodes.triggerSchedule.invalidExecutionTime')
|
|
|
|
if (executionTime <= new Date())
|
|
return t('workflow.nodes.triggerSchedule.executionTimeMustBeFuture')
|
|
|
|
return ''
|
|
}
|
|
|
|
const validateVisualConfig = (payload: ScheduleTriggerNodeType, t: any): string => {
|
|
const i18nPrefix = 'workflow.errorMsg'
|
|
const { visual_config } = payload
|
|
|
|
if (!visual_config)
|
|
return t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.triggerSchedule.visualConfig') })
|
|
|
|
switch (payload.frequency) {
|
|
case 'hourly':
|
|
return validateHourlyConfig(visual_config, t)
|
|
case 'daily':
|
|
return validateDailyConfig(visual_config, t)
|
|
case 'weekly':
|
|
return validateWeeklyConfig(visual_config, t)
|
|
case 'monthly':
|
|
return validateMonthlyConfig(visual_config, t)
|
|
case 'once':
|
|
return validateOnceConfig(visual_config, t)
|
|
default:
|
|
return t('workflow.nodes.triggerSchedule.invalidFrequency')
|
|
}
|
|
}
|
|
|
|
const nodeDefault: NodeDefault<ScheduleTriggerNodeType> = {
|
|
defaultValue: {
|
|
mode: 'visual',
|
|
frequency: 'daily',
|
|
cron_expression: '',
|
|
visual_config: {
|
|
time: '11:30 AM',
|
|
weekdays: ['sun'],
|
|
},
|
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
enabled: true,
|
|
},
|
|
getAvailablePrevNodes(_isChatMode: boolean) {
|
|
return []
|
|
},
|
|
getAvailableNextNodes(isChatMode: boolean) {
|
|
const nodes = isChatMode
|
|
? ALL_CHAT_AVAILABLE_BLOCKS
|
|
: ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End)
|
|
return nodes.filter(type => type !== BlockEnum.Start)
|
|
},
|
|
checkValid(payload: ScheduleTriggerNodeType, t: any) {
|
|
const i18nPrefix = 'workflow.errorMsg'
|
|
let errorMessages = ''
|
|
if (!errorMessages && !payload.mode)
|
|
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.triggerSchedule.mode') })
|
|
|
|
if (!errorMessages && !payload.timezone)
|
|
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.triggerSchedule.timezone') })
|
|
if (!errorMessages && payload.timezone) {
|
|
try {
|
|
Intl.DateTimeFormat(undefined, { timeZone: payload.timezone })
|
|
}
|
|
catch {
|
|
errorMessages = t('workflow.nodes.triggerSchedule.invalidTimezone')
|
|
}
|
|
}
|
|
if (!errorMessages) {
|
|
if (payload.mode === 'cron') {
|
|
if (!payload.cron_expression || payload.cron_expression.trim() === '')
|
|
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.triggerSchedule.cronExpression') })
|
|
else if (!isValidCronExpression(payload.cron_expression))
|
|
errorMessages = t('workflow.nodes.triggerSchedule.invalidCronExpression')
|
|
}
|
|
else if (payload.mode === 'visual') {
|
|
if (!payload.frequency)
|
|
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.triggerSchedule.frequency') })
|
|
else
|
|
errorMessages = validateVisualConfig(payload, t)
|
|
}
|
|
}
|
|
if (!errorMessages) {
|
|
try {
|
|
const nextTimes = getNextExecutionTimes(payload, 1)
|
|
if (nextTimes.length === 0)
|
|
errorMessages = t('workflow.nodes.triggerSchedule.noValidExecutionTime')
|
|
}
|
|
catch {
|
|
errorMessages = t('workflow.nodes.triggerSchedule.executionTimeCalculationError')
|
|
}
|
|
}
|
|
|
|
return {
|
|
isValid: !errorMessages,
|
|
errorMessage: errorMessages,
|
|
}
|
|
},
|
|
}
|
|
|
|
export default nodeDefault
|