(
targetId: string,
workspaceId: string
)
| 201 | * validated against the live flowData (not the stored cron which may be a fallback). |
| 202 | */ |
| 203 | const getScheduleStatus = async ( |
| 204 | targetId: string, |
| 205 | workspaceId: string |
| 206 | ): Promise<{ record: ScheduleRecord | null; canEnable: boolean; reason?: string }> => { |
| 207 | try { |
| 208 | const appServer = getRunningExpressApp() |
| 209 | const record = await appServer.AppDataSource.getRepository(ScheduleRecord).findOne({ |
| 210 | where: { targetId, triggerType: ScheduleTriggerType.AGENTFLOW, workspaceId } |
| 211 | }) |
| 212 | |
| 213 | const chatflow = await appServer.AppDataSource.getRepository(ChatFlow).findOne({ |
| 214 | where: { id: targetId, workspaceId } |
| 215 | }) |
| 216 | if (!chatflow?.flowData) { |
| 217 | return { record, canEnable: false, reason: 'Flow not found or has no data' } |
| 218 | } |
| 219 | |
| 220 | try { |
| 221 | const parsedFlowData = JSON.parse(chatflow.flowData) |
| 222 | const startNode = (parsedFlowData.nodes || []).find((n: any) => n.data?.name === 'startAgentflow') |
| 223 | const startInputType = startNode?.data?.inputs?.startInputType as StartInputType | undefined |
| 224 | if (!startNode || startInputType !== 'scheduleInput') { |
| 225 | return { record, canEnable: false, reason: 'Flow is not configured as a scheduled flow' } |
| 226 | } |
| 227 | |
| 228 | const inputs = startNode.data.inputs as Record<string, any> |
| 229 | const cronResult = resolveScheduleCron(inputs) |
| 230 | if (!cronResult.valid) { |
| 231 | return { record, canEnable: false, reason: cronResult.error || 'Invalid cron expression or timezone' } |
| 232 | } |
| 233 | |
| 234 | // endDate must be in the future if set |
| 235 | const endDateValue = inputs.scheduleEndDate || record?.endDate |
| 236 | if (endDateValue) { |
| 237 | const endDate = new Date(endDateValue) |
| 238 | if (isNaN(endDate.getTime())) { |
| 239 | return { record, canEnable: false, reason: 'Invalid end date' } |
| 240 | } |
| 241 | if (endDate <= new Date()) { |
| 242 | return { record, canEnable: false, reason: 'End date is in the past' } |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | // Validate input presence according to the chosen schedule input mode. |
| 247 | // 'text' requires a non-empty default input; 'form' requires at least one form field; 'none' is always valid. |
| 248 | const mode = (inputs.scheduleInputMode as ScheduleInputMode) ?? record?.scheduleInputMode |
| 249 | if (!mode) { |
| 250 | return { record, canEnable: false, reason: 'Schedule Input Mode is required' } |
| 251 | } |
| 252 | const isInputValidResult = isScheduleInputValid(mode, inputs.scheduleDefaultInput, inputs.scheduleFormInputTypes) |
| 253 | if (!isInputValidResult) { |
| 254 | const reason = |
| 255 | mode === 'form' |
| 256 | ? 'At least one form field must be defined to enable schedule' |
| 257 | : 'Default input is required to enable schedule' |
| 258 | return { record, canEnable: false, reason } |
| 259 | } |
| 260 |
no test coverage detected