feat(cron): add scheduler status endpoint
This commit is contained in:
@@ -59,6 +59,7 @@ export class CronService {
|
||||
private timer: NodeJS.Timeout | null = null;
|
||||
private running = false;
|
||||
private op: Promise<unknown> = Promise.resolve();
|
||||
private warnedDisabled = false;
|
||||
|
||||
constructor(deps: CronServiceDeps) {
|
||||
this.deps = {
|
||||
@@ -94,6 +95,19 @@ export class CronService {
|
||||
this.timer = null;
|
||||
}
|
||||
|
||||
async status() {
|
||||
return await this.locked(async () => {
|
||||
await this.ensureLoaded();
|
||||
return {
|
||||
enabled: this.deps.cronEnabled,
|
||||
storePath: this.deps.storePath,
|
||||
jobs: this.store?.jobs.length ?? 0,
|
||||
nextWakeAtMs:
|
||||
this.deps.cronEnabled === true ? (this.nextWakeAtMs() ?? null) : null,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async list(opts?: { includeDisabled?: boolean }) {
|
||||
return await this.locked(async () => {
|
||||
await this.ensureLoaded();
|
||||
@@ -109,6 +123,7 @@ export class CronService {
|
||||
|
||||
async add(input: CronJobCreate) {
|
||||
return await this.locked(async () => {
|
||||
this.warnIfDisabled("add");
|
||||
await this.ensureLoaded();
|
||||
const now = this.deps.nowMs();
|
||||
const id = crypto.randomUUID();
|
||||
@@ -142,6 +157,7 @@ export class CronService {
|
||||
|
||||
async update(id: string, patch: CronJobPatch) {
|
||||
return await this.locked(async () => {
|
||||
this.warnIfDisabled("update");
|
||||
await this.ensureLoaded();
|
||||
const job = this.findJobOrThrow(id);
|
||||
const now = this.deps.nowMs();
|
||||
@@ -176,6 +192,7 @@ export class CronService {
|
||||
|
||||
async remove(id: string) {
|
||||
return await this.locked(async () => {
|
||||
this.warnIfDisabled("remove");
|
||||
await this.ensureLoaded();
|
||||
const before = this.store?.jobs.length ?? 0;
|
||||
if (!this.store) return { ok: false, removed: false };
|
||||
@@ -190,6 +207,7 @@ export class CronService {
|
||||
|
||||
async run(id: string, mode?: "due" | "force") {
|
||||
return await this.locked(async () => {
|
||||
this.warnIfDisabled("run");
|
||||
await this.ensureLoaded();
|
||||
const job = this.findJobOrThrow(id);
|
||||
const now = this.deps.nowMs();
|
||||
@@ -232,6 +250,16 @@ export class CronService {
|
||||
this.store = { version: 1, jobs: loaded.jobs ?? [] };
|
||||
}
|
||||
|
||||
private warnIfDisabled(action: string) {
|
||||
if (this.deps.cronEnabled) return;
|
||||
if (this.warnedDisabled) return;
|
||||
this.warnedDisabled = true;
|
||||
this.deps.log.warn(
|
||||
{ enabled: false, action, storePath: this.deps.storePath },
|
||||
"cron: scheduler disabled; jobs will not run automatically",
|
||||
);
|
||||
}
|
||||
|
||||
private async persist() {
|
||||
if (!this.store) return;
|
||||
await saveCronStore(this.deps.storePath, this.store);
|
||||
|
||||
@@ -22,6 +22,8 @@ import {
|
||||
CronRunParamsSchema,
|
||||
type CronRunsParams,
|
||||
CronRunsParamsSchema,
|
||||
type CronStatusParams,
|
||||
CronStatusParamsSchema,
|
||||
type CronUpdateParams,
|
||||
CronUpdateParamsSchema,
|
||||
ErrorCodes,
|
||||
@@ -74,6 +76,9 @@ export const validateAgentParams = ajv.compile(AgentParamsSchema);
|
||||
export const validateWakeParams = ajv.compile<WakeParams>(WakeParamsSchema);
|
||||
export const validateCronListParams =
|
||||
ajv.compile<CronListParams>(CronListParamsSchema);
|
||||
export const validateCronStatusParams = ajv.compile<CronStatusParams>(
|
||||
CronStatusParamsSchema,
|
||||
);
|
||||
export const validateCronAddParams =
|
||||
ajv.compile<CronAddParams>(CronAddParamsSchema);
|
||||
export const validateCronUpdateParams = ajv.compile<CronUpdateParams>(
|
||||
@@ -115,6 +120,7 @@ export {
|
||||
WakeParamsSchema,
|
||||
CronJobSchema,
|
||||
CronListParamsSchema,
|
||||
CronStatusParamsSchema,
|
||||
CronAddParamsSchema,
|
||||
CronUpdateParamsSchema,
|
||||
CronRemoveParamsSchema,
|
||||
@@ -148,6 +154,7 @@ export type {
|
||||
WakeParams,
|
||||
CronJob,
|
||||
CronListParams,
|
||||
CronStatusParams,
|
||||
CronAddParams,
|
||||
CronUpdateParams,
|
||||
CronRemoveParams,
|
||||
|
||||
@@ -316,6 +316,11 @@ export const CronListParamsSchema = Type.Object(
|
||||
{ additionalProperties: false },
|
||||
);
|
||||
|
||||
export const CronStatusParamsSchema = Type.Object(
|
||||
{},
|
||||
{ additionalProperties: false },
|
||||
);
|
||||
|
||||
export const CronAddParamsSchema = Type.Object(
|
||||
{
|
||||
name: Type.Optional(Type.String()),
|
||||
@@ -438,6 +443,7 @@ export const ProtocolSchemas: Record<string, TSchema> = {
|
||||
WakeParams: WakeParamsSchema,
|
||||
CronJob: CronJobSchema,
|
||||
CronListParams: CronListParamsSchema,
|
||||
CronStatusParams: CronStatusParamsSchema,
|
||||
CronAddParams: CronAddParamsSchema,
|
||||
CronUpdateParams: CronUpdateParamsSchema,
|
||||
CronRemoveParams: CronRemoveParamsSchema,
|
||||
@@ -467,6 +473,7 @@ export type AgentEvent = Static<typeof AgentEventSchema>;
|
||||
export type WakeParams = Static<typeof WakeParamsSchema>;
|
||||
export type CronJob = Static<typeof CronJobSchema>;
|
||||
export type CronListParams = Static<typeof CronListParamsSchema>;
|
||||
export type CronStatusParams = Static<typeof CronStatusParamsSchema>;
|
||||
export type CronAddParams = Static<typeof CronAddParamsSchema>;
|
||||
export type CronUpdateParams = Static<typeof CronUpdateParamsSchema>;
|
||||
export type CronRemoveParams = Static<typeof CronRemoveParamsSchema>;
|
||||
|
||||
@@ -76,6 +76,7 @@ import {
|
||||
validateCronRemoveParams,
|
||||
validateCronRunParams,
|
||||
validateCronRunsParams,
|
||||
validateCronStatusParams,
|
||||
validateCronUpdateParams,
|
||||
validateRequestFrame,
|
||||
validateSendParams,
|
||||
@@ -96,6 +97,7 @@ const METHODS = [
|
||||
"set-heartbeats",
|
||||
"wake",
|
||||
"cron.list",
|
||||
"cron.status",
|
||||
"cron.add",
|
||||
"cron.update",
|
||||
"cron.remove",
|
||||
@@ -1116,6 +1118,23 @@ export async function startGatewayServer(
|
||||
respond(true, { jobs }, undefined);
|
||||
break;
|
||||
}
|
||||
case "cron.status": {
|
||||
const params = (req.params ?? {}) as Record<string, unknown>;
|
||||
if (!validateCronStatusParams(params)) {
|
||||
respond(
|
||||
false,
|
||||
undefined,
|
||||
errorShape(
|
||||
ErrorCodes.INVALID_REQUEST,
|
||||
`invalid cron.status params: ${formatValidationErrors(validateCronStatusParams.errors)}`,
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
const status = await cron.status();
|
||||
respond(true, status, undefined);
|
||||
break;
|
||||
}
|
||||
case "cron.add": {
|
||||
const params = (req.params ?? {}) as Record<string, unknown>;
|
||||
if (!validateCronAddParams(params)) {
|
||||
|
||||
Reference in New Issue
Block a user