Socialify

Folder ..

Viewing TaskManager.ts
192 lines (169 loc) • 5.8 KB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import { Repository } from 'typeorm';
import Logger from '../utils/logger';
import { TaskLog } from '../entities/TaskLog';
import AppDataSource from '../database/data-source';

export interface Task {
    name: string;
    interval: number; // in milliseconds
    execute: () => Promise<void>;
    lastRun?: Date;
}

export class TaskManager {
    private tasks: Map<string, Task> = new Map();
    private intervals: Map<string, NodeJS.Timeout> = new Map();
    private taskLogRepository: Repository<TaskLog>;

    constructor() {
        this.taskLogRepository = AppDataSource.getRepository(TaskLog);
    }

    registerTask(task: Task): void {
        if (this.tasks.has(task.name)) {
            throw new Error(`Task ${task.name} already registered`);
        }

        this.tasks.set(task.name, task);
        Logger.info(`Task ${task.name} registered`, {
            timestamp: true,
            prefix: 'TaskManager'
        });
    }

    private async shouldExecuteTask(taskName: string): Promise<boolean> {
        const lastExecution = await this.taskLogRepository.findOne({
            where: { taskName },
            order: { executedAt: 'DESC' }
        });

        if (!lastExecution) {
            return true;
        }

        const task = this.tasks.get(taskName);
        if (!task) {
            return false;
        }

        const timeSinceLastRun = Date.now() - lastExecution.executedAt.getTime();
        return timeSinceLastRun >= task.interval;
    }

    private async logTaskExecution(taskName: string, status: 'success' | 'error', error?: string): Promise<void> {
        const taskLog = this.taskLogRepository.create({
            taskName,
            status,
            error
        });
        await this.taskLogRepository.save(taskLog);
    }

    async startTask(taskName: string): Promise<void> {
        const task = this.tasks.get(taskName);
        if (!task) {
            throw new Error(`Task ${taskName} not found`);
        }

        // Check if task should run
        const shouldExecute = await this.shouldExecuteTask(taskName);
        if (!shouldExecute) {
            Logger.info(`Skipping task ${taskName} - not due yet`, {
                timestamp: true,
                prefix: 'TaskManager'
            });
            return;
        }

        // Clear any existing interval
        this.stopTask(taskName);

        // Execute task
        try {
            await task.execute();
            task.lastRun = new Date();
            await this.logTaskExecution(taskName, 'success');
            Logger.success(`Task ${taskName} executed successfully`, {
                timestamp: true,
                prefix: 'TaskManager'
            });
        } catch (error) {
            await this.logTaskExecution(taskName, 'error', error instanceof Error ? error.message : String(error));
            Logger.error(`Task ${taskName} failed: ${error}`, {
                timestamp: true,
                prefix: 'TaskManager'
            });
        }

        // Schedule next run
        const interval = setInterval(async () => {
            try {
                await task.execute();
                task.lastRun = new Date();
                await this.logTaskExecution(taskName, 'success');
                Logger.success(`Task ${taskName} executed successfully`, {
                    timestamp: true,
                    prefix: 'TaskManager'
                });
            } catch (error) {
                await this.logTaskExecution(taskName, 'error', error instanceof Error ? error.message : String(error));
                Logger.error(`Task ${taskName} failed: ${error}`, {
                    timestamp: true,
                    prefix: 'TaskManager'
                });
            }
        }, task.interval);

        this.intervals.set(taskName, interval);
        Logger.info(`Task ${taskName} scheduled with interval ${task.interval}ms`, {
            timestamp: true,
            prefix: 'TaskManager'
        });
    }

    stopTask(taskName: string): void {
        const interval = this.intervals.get(taskName);
        if (interval) {
            clearInterval(interval);
            this.intervals.delete(taskName);
            Logger.info(`Task ${taskName} stopped`, {
                timestamp: true,
                prefix: 'TaskManager'
            });
        }
    }

    async startAllTasks(): Promise<void> {
        for (const taskName of this.tasks.keys()) {
            await this.startTask(taskName);
        }
    }

    stopAllTasks(): void {
        for (const taskName of this.intervals.keys()) {
            this.stopTask(taskName);
        }
    }

    async getTaskStatus(taskName: string): Promise<{
        registered: boolean;
        running: boolean;
        lastRun?: Date;
        nextRun?: Date;
    }> {
        const task = this.tasks.get(taskName);
        const interval = this.intervals.get(taskName);
        const lastExecution = await this.taskLogRepository.findOne({
            where: { taskName },
            order: { executedAt: 'DESC' }
        });

        let nextRun: Date | undefined;
        if (lastExecution) {
            nextRun = new Date(lastExecution.executedAt.getTime() + (task?.interval || 0));
        }

        return {
            registered: !!task,
            running: !!interval,
            lastRun: lastExecution?.executedAt,
            nextRun
        };
    }

    async getAllTasksStatus(): Promise<Record<string, {
        registered: boolean;
        running: boolean;
        lastRun?: Date;
        nextRun?: Date;
    }>> {
        const status: Record<string, {
            registered: boolean;
            running: boolean;
            lastRun?: Date;
            nextRun?: Date;
        }> = {};
        for (const [taskName] of this.tasks) {
            status[taskName] = await this.getTaskStatus(taskName);
        }
        return status;
    }
}

export const taskManager = new TaskManager();