arcadiasuite/server/automations/service.ts

209 lines
6.4 KiB
TypeScript

import { db } from "../../db/index";
import { automations, automationActions, automationLogs, scheduledTasks, manusRuns } from "@shared/schema";
import { eq, desc, lte, and, isNotNull } from "drizzle-orm";
import { manusService } from "../manus/service";
export interface AutomationResult {
success: boolean;
logId: number;
result?: string;
error?: string;
}
class AutomationService {
private schedulerInterval: NodeJS.Timeout | null = null;
async runAutomation(automationId: number, userId: string, triggerData?: any): Promise<AutomationResult> {
const [log] = await db.insert(automationLogs).values({
automationId,
status: "running",
triggerData: triggerData ? JSON.stringify(triggerData) : null,
}).returning();
try {
const [automation] = await db.select()
.from(automations)
.where(eq(automations.id, automationId));
if (!automation) {
throw new Error("Automação não encontrada");
}
const actions = await db.select()
.from(automationActions)
.where(eq(automationActions.automationId, automationId))
.orderBy(automationActions.orderIndex);
const results: string[] = [];
for (const action of actions) {
const actionResult = await this.executeAction(action, userId, triggerData);
results.push(`${action.actionType}: ${actionResult}`);
}
const finalResult = results.join("\n");
await db.update(automationLogs)
.set({
status: "completed",
result: finalResult,
completedAt: new Date(),
})
.where(eq(automationLogs.id, log.id));
return { success: true, logId: log.id, result: finalResult };
} catch (error: any) {
await db.update(automationLogs)
.set({
status: "error",
error: error.message,
completedAt: new Date(),
})
.where(eq(automationLogs.id, log.id));
return { success: false, logId: log.id, error: error.message };
}
}
private async executeAction(action: any, userId: string, triggerData?: any): Promise<string> {
const config = action.actionConfig ? JSON.parse(action.actionConfig) : {};
switch (action.actionType) {
case "agent_task":
return await this.executeAgentTask(userId, config.prompt || "Execute automation task");
case "send_notification":
return `Notificação enviada: ${config.message || "Automação executada"}`;
case "erp_sync":
return `Sincronização ERP iniciada para: ${config.entity || "todos"}`;
case "generate_report":
return `Relatório gerado: ${config.reportType || "resumo"}`;
case "webhook":
if (config.url) {
try {
const response = await fetch(config.url, {
method: config.method || "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ automationId: action.automationId, triggerData, config }),
});
return `Webhook chamado: ${response.status}`;
} catch (e: any) {
return `Webhook falhou: ${e.message}`;
}
}
return "URL do webhook não configurada";
case "send_email":
return `Email enviado para: ${config.to || "não especificado"}`;
case "send_whatsapp":
return `Mensagem WhatsApp enviada para: ${config.to || "não especificado"}`;
default:
return `Tipo de ação desconhecido: ${action.actionType}`;
}
}
private async executeAgentTask(userId: string, prompt: string): Promise<string> {
try {
const { runId } = await manusService.run(userId, prompt);
const maxWaitTime = 120000;
const pollInterval = 2000;
const startTime = Date.now();
while (Date.now() - startTime < maxWaitTime) {
await new Promise(resolve => setTimeout(resolve, pollInterval));
const [run] = await db.select()
.from(manusRuns)
.where(eq(manusRuns.id, runId));
if (!run) {
return `Tarefa do agente #${runId} não encontrada`;
}
if (run.status === "completed") {
return run.result || `Tarefa do agente #${runId} concluída com sucesso`;
}
if (run.status === "stopped") {
return run.result || `Tarefa do agente #${runId} parada (máximo de passos atingido)`;
}
if (run.status === "error") {
return `Tarefa do agente #${runId} falhou: ${run.result || "erro desconhecido"}`;
}
}
return `Tarefa do agente #${runId} iniciada (aguardando conclusão em segundo plano)`;
} catch (error: any) {
return `Erro ao executar tarefa do agente: ${error.message}`;
}
}
startScheduler() {
if (this.schedulerInterval) return;
this.schedulerInterval = setInterval(async () => {
try {
await this.checkScheduledTasks();
} catch (error) {
console.error("Scheduler error:", error);
}
}, 60000);
console.log("Automation scheduler started");
}
stopScheduler() {
if (this.schedulerInterval) {
clearInterval(this.schedulerInterval);
this.schedulerInterval = null;
console.log("Automation scheduler stopped");
}
}
private async checkScheduledTasks() {
const now = new Date();
const dueTasks = await db.select({
task: scheduledTasks,
automation: automations,
})
.from(scheduledTasks)
.innerJoin(automations, eq(scheduledTasks.automationId, automations.id))
.where(and(
eq(scheduledTasks.isActive, "true"),
eq(automations.isActive, "true"),
isNotNull(scheduledTasks.nextRunAt),
lte(scheduledTasks.nextRunAt, now)
));
for (const { task, automation } of dueTasks) {
try {
await this.runAutomation(automation.id, automation.userId, { scheduled: true });
let nextRun: Date | null = null;
if (task.intervalMinutes) {
nextRun = new Date(now.getTime() + task.intervalMinutes * 60000);
}
await db.update(scheduledTasks)
.set({
lastRunAt: now,
nextRunAt: nextRun,
})
.where(eq(scheduledTasks.id, task.id));
} catch (error) {
console.error(`Error running scheduled automation ${automation.id}:`, error);
}
}
}
}
export const automationService = new AutomationService();