feat: Implement Cloudly Task Manager with predefined tasks and execution tracking
- Added CloudlyTaskManager class for managing tasks, including registration, execution, scheduling, and cancellation. - Created predefined tasks: DNS Sync, Certificate Renewal, Cleanup, Health Check, Resource Report, Database Maintenance, Security Scan, and Docker Cleanup. - Introduced ITaskExecution interface for tracking task execution details and outcomes. - Developed API request interfaces for task management operations (getTasks, getTaskExecutions, triggerTask, cancelTask). - Implemented CloudlyViewTasks web component for displaying tasks and their execution history, including filtering and detailed views.
This commit is contained in:
165
ts/manager.task/classes.taskexecution.ts
Normal file
165
ts/manager.task/classes.taskexecution.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import * as plugins from '../plugins.js';
|
||||
import { CloudlyTaskManager } from './classes.taskmanager.js';
|
||||
|
||||
@plugins.smartdata.managed()
|
||||
export class TaskExecution extends plugins.smartdata.SmartDataDbDoc<
|
||||
TaskExecution,
|
||||
plugins.servezoneInterfaces.data.ITaskExecution,
|
||||
CloudlyTaskManager
|
||||
> {
|
||||
// STATIC
|
||||
public static async getTaskExecutionById(executionIdArg: string) {
|
||||
const execution = await this.getInstance({
|
||||
id: executionIdArg,
|
||||
});
|
||||
return execution;
|
||||
}
|
||||
|
||||
public static async getTaskExecutions(filterArg?: {
|
||||
taskName?: string;
|
||||
status?: string;
|
||||
startedAfter?: number;
|
||||
startedBefore?: number;
|
||||
}) {
|
||||
const query: any = {};
|
||||
|
||||
if (filterArg?.taskName) {
|
||||
query['data.taskName'] = filterArg.taskName;
|
||||
}
|
||||
if (filterArg?.status) {
|
||||
query['data.status'] = filterArg.status;
|
||||
}
|
||||
if (filterArg?.startedAfter || filterArg?.startedBefore) {
|
||||
query['data.startedAt'] = {};
|
||||
if (filterArg.startedAfter) {
|
||||
query['data.startedAt'].$gte = filterArg.startedAfter;
|
||||
}
|
||||
if (filterArg.startedBefore) {
|
||||
query['data.startedAt'].$lte = filterArg.startedBefore;
|
||||
}
|
||||
}
|
||||
|
||||
const executions = await this.getInstances(query);
|
||||
return executions;
|
||||
}
|
||||
|
||||
public static async createTaskExecution(
|
||||
taskName: string,
|
||||
triggeredBy: 'schedule' | 'manual' | 'system',
|
||||
userId?: string
|
||||
) {
|
||||
const execution = new TaskExecution();
|
||||
execution.id = await TaskExecution.getNewId();
|
||||
execution.data = {
|
||||
taskName,
|
||||
startedAt: Date.now(),
|
||||
status: 'running',
|
||||
triggeredBy,
|
||||
userId,
|
||||
logs: [],
|
||||
};
|
||||
await execution.save();
|
||||
return execution;
|
||||
}
|
||||
|
||||
// INSTANCE
|
||||
@plugins.smartdata.svDb()
|
||||
public id: string;
|
||||
|
||||
@plugins.smartdata.svDb()
|
||||
public data: plugins.servezoneInterfaces.data.ITaskExecution['data'];
|
||||
|
||||
/**
|
||||
* Add a log entry to the execution
|
||||
*/
|
||||
public async addLog(message: string, severity: 'info' | 'warning' | 'error' | 'success' = 'info') {
|
||||
this.data.logs.push({
|
||||
timestamp: Date.now(),
|
||||
message,
|
||||
severity,
|
||||
});
|
||||
await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a metric for the execution
|
||||
*/
|
||||
public async setMetric(key: string, value: any) {
|
||||
if (!this.data.metrics) {
|
||||
this.data.metrics = {};
|
||||
}
|
||||
this.data.metrics[key] = value;
|
||||
await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the execution as completed
|
||||
*/
|
||||
public async complete(result?: any) {
|
||||
this.data.completedAt = Date.now();
|
||||
this.data.duration = this.data.completedAt - this.data.startedAt;
|
||||
this.data.status = 'completed';
|
||||
if (result !== undefined) {
|
||||
this.data.result = result;
|
||||
}
|
||||
await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the execution as failed
|
||||
*/
|
||||
public async fail(error: Error | string) {
|
||||
this.data.completedAt = Date.now();
|
||||
this.data.duration = this.data.completedAt - this.data.startedAt;
|
||||
this.data.status = 'failed';
|
||||
|
||||
if (typeof error === 'string') {
|
||||
this.data.error = {
|
||||
message: error,
|
||||
};
|
||||
} else {
|
||||
this.data.error = {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
code: (error as any).code,
|
||||
};
|
||||
}
|
||||
|
||||
await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the execution
|
||||
*/
|
||||
public async cancel() {
|
||||
this.data.completedAt = Date.now();
|
||||
this.data.duration = this.data.completedAt - this.data.startedAt;
|
||||
this.data.status = 'cancelled';
|
||||
await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get running executions
|
||||
*/
|
||||
public static async getRunningExecutions() {
|
||||
return await this.getInstances({
|
||||
'data.status': 'running',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up old executions
|
||||
*/
|
||||
public static async cleanupOldExecutions(olderThanDays: number = 30) {
|
||||
const cutoffTime = Date.now() - (olderThanDays * 24 * 60 * 60 * 1000);
|
||||
const oldExecutions = await this.getInstances({
|
||||
'data.completedAt': { $lt: cutoffTime },
|
||||
});
|
||||
|
||||
for (const execution of oldExecutions) {
|
||||
await execution.delete();
|
||||
}
|
||||
|
||||
return oldExecutions.length;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user