From 825116ecdc85504fbb2ca11ec3e1e8c92deab6bd Mon Sep 17 00:00:00 2001 From: npmrun <1549469775@qq.com> Date: Thu, 14 May 2026 14:55:48 +0800 Subject: [PATCH] feat: add API endpoints to get, update, and delete tasks Co-Authored-By: Claude Opus 4.7 --- server/api/scheduler/tasks/[id].delete.ts | 12 +++++++ server/api/scheduler/tasks/[id].get.ts | 13 +++++++ server/api/scheduler/tasks/[id].put.ts | 58 +++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 server/api/scheduler/tasks/[id].delete.ts create mode 100644 server/api/scheduler/tasks/[id].get.ts create mode 100644 server/api/scheduler/tasks/[id].put.ts diff --git a/server/api/scheduler/tasks/[id].delete.ts b/server/api/scheduler/tasks/[id].delete.ts new file mode 100644 index 0000000..a2e7fbe --- /dev/null +++ b/server/api/scheduler/tasks/[id].delete.ts @@ -0,0 +1,12 @@ +import { deleteTask } from "../../../service/scheduler"; +import { removeTask } from "../../../scheduler/engine"; + +export default defineWrappedResponseHandler(async (event) => { + const id = getRouterParam(event, "id"); + if (!id) return R.throwError(400, "Missing id", null); + + removeTask(id); + await deleteTask(id); + + return R.success(null); +}); diff --git a/server/api/scheduler/tasks/[id].get.ts b/server/api/scheduler/tasks/[id].get.ts new file mode 100644 index 0000000..de66dce --- /dev/null +++ b/server/api/scheduler/tasks/[id].get.ts @@ -0,0 +1,13 @@ +import { getTaskById, getRecentExecutions } from "../../../service/scheduler"; + +export default defineWrappedResponseHandler(async (event) => { + const id = getRouterParam(event, "id"); + if (!id) return R.throwError(400, "Missing id", null); + + const task = await getTaskById(id); + if (!task) return R.throwError(404, "Task not found", null); + + const recentExecutions = await getRecentExecutions(id, 20); + + return R.success({ task, recentExecutions }); +}); diff --git a/server/api/scheduler/tasks/[id].put.ts b/server/api/scheduler/tasks/[id].put.ts new file mode 100644 index 0000000..b13ef0c --- /dev/null +++ b/server/api/scheduler/tasks/[id].put.ts @@ -0,0 +1,58 @@ +import { z } from "zod"; +import { updateTask, getTaskById } from "../../../service/scheduler"; +import { reloadTask } from "../../../scheduler/engine"; +import { Cron } from "croner"; + +const updateTaskSchema = z.object({ + name: z.string().min(1).optional(), + cronExpression: z.string().min(1).optional(), + type: z.enum(["function", "http"]).optional(), + functionName: z.string().optional().nullable(), + functionPayload: z.string().optional().nullable(), + httpMethod: z.enum(["GET", "POST", "PUT", "DELETE"]).optional().nullable(), + httpUrl: z.string().optional().nullable(), + httpHeaders: z.string().optional().nullable(), + httpBody: z.string().optional().nullable(), + catchUp: z.union([z.boolean(), z.number()]).optional(), + enabled: z.union([z.boolean(), z.number()]).optional(), + maxRetries: z.number().int().min(0).optional(), + retryDelaySeconds: z.number().int().min(1).optional(), + timeoutSeconds: z.number().int().min(1).optional(), +}); + +export default defineWrappedResponseHandler(async (event) => { + const id = getRouterParam(event, "id"); + if (!id) return R.throwError(400, "Missing id", null); + + const body = await readBody(event); + const parsed = updateTaskSchema.safeParse(body); + if (!parsed.success) { + return R.throwError(422, "Validation failed", parsed.error.issues); + } + + const existing = await getTaskById(id); + if (!existing) return R.throwError(404, "Task not found", null); + + // Validate cron if provided + const cronExpr = parsed.data.cronExpression ?? existing.cronExpression; + try { + new Cron(cronExpr); + } catch { + return R.throwError(422, "Invalid cron expression", null); + } + + const updateData: Record = { ...parsed.data }; + if (parsed.data.catchUp !== undefined) { + updateData.catchUp = parsed.data.catchUp ? 1 : 0; + } + if (parsed.data.enabled !== undefined) { + updateData.enabled = parsed.data.enabled ? 1 : 0; + } + + const task = await updateTask(id, updateData as Parameters[1]); + if (task) { + reloadTask(task.id); + } + + return R.success(task); +});