Server Actions

api
server-actions
typescript
reference

API completa de Server Actions do Next.js com TypeScript e validação Zod

Por Tarefa AI Team17/01/2025
Nesta Página

Server Actions API

Tarefa AI usa Next.js Server Actions para fornecer uma API tipada e segura. Todas as actions incluem validação Zod, tratamento de erros e rate limiting.

Visão Geral

Server Actions são funções assíncronas que rodam no servidor e podem ser chamadas diretamente de componentes React.

Vantagens

  • Type-safe: TypeScript end-to-end
  • Validação: Zod schemas para todos os inputs
  • Segurança: Rate limiting e autenticação integrados
  • Performance: Otimização automática pelo Next.js

Autenticação

Todas as actions que requerem autenticação verificam a sessão automaticamente.

import { auth } from '@/lib/auth';
 
export async function protectedAction() {
  'use server';
 
  const session = await auth();
  if (!session?.user) {
    throw new Error('Não autenticado');
  }
 
  // Sua lógica aqui
}

Task Actions

createTask

Cria uma nova tarefa agendada.

import { createTask } from '@/app/actions/task-actions';
 
const result = await createTask({
  name: 'Resumo Diário',
  description: 'Gerar resumo de notícias',
  prompt: 'Resuma as principais notícias de tech do dia',
  model: 'google/gemini-2.5-pro',
  recurrence: '0 9 * * *', // Diário às 9:00
  active: true,
});

Schema de Validação:

const createTaskSchema = z.object({
  name: z.string().min(3).max(100),
  description: z.string().max(500).optional(),
  prompt: z.string().min(10),
  model: z.string(),
  recurrence: z.string(), // Cron expression
  active: z.boolean().default(true),
  webhookUrl: z.string().url().optional(),
  notificationEmail: z.string().email().optional(),
});

Resposta:

{
  success: true,
  data: {
    id: 'task_123',
    name: 'Resumo Diário',
    createdAt: '2025-01-17T10:00:00Z'
  }
}

updateTask

Atualiza uma tarefa existente.

const result = await updateTask({
  id: 'task_123',
  name: 'Novo Nome',
  prompt: 'Novo prompt',
  active: false,
});

Validação:

const updateTaskSchema = z.object({
  id: z.string(),
  name: z.string().min(3).max(100).optional(),
  description: z.string().max(500).optional(),
  prompt: z.string().min(10).optional(),
  model: z.string().optional(),
  recurrence: z.string().optional(),
  active: z.boolean().optional(),
});

deleteTask

Remove uma tarefa permanentemente.

const result = await deleteTask('task_123');

Comportamento:

  • Cancela execuções pendentes
  • Remove histórico de execuções
  • Operação irreversível

toggleTaskActive

Ativa ou pausa uma tarefa.

const result = await toggleTaskActive('task_123', false); // Pausar

executeTask

Executa uma tarefa imediatamente (fora do agendamento).

const result = await executeTask('task_123');

Resposta:

{
  success: true,
  data: {
    executionId: 'exec_456',
    status: 'PENDING',
    startedAt: '2025-01-17T10:30:00Z'
  }
}

duplicateTask

Cria uma cópia de uma tarefa existente.

const result = await duplicateTask('task_123', {
  name: 'Cópia - Resumo Diário',
});

Execution Actions

getExecutions

Lista execuções de uma tarefa.

import { getExecutions } from '@/app/actions/execution-actions';
 
const executions = await getExecutions({
  taskId: 'task_123',
  status: 'SUCCESS',
  limit: 20,
  offset: 0,
});

Schema:

const getExecutionsSchema = z.object({
  taskId: z.string().optional(),
  status: z.enum(['PENDING', 'RUNNING', 'SUCCESS', 'FAILED']).optional(),
  startDate: z.date().optional(),
  endDate: z.date().optional(),
  limit: z.number().min(1).max(100).default(20),
  offset: z.number().min(0).default(0),
});

Resposta:

{
  executions: [
    {
      id: 'exec_456',
      taskId: 'task_123',
      status: 'SUCCESS',
      startedAt: '2025-01-17T10:00:00Z',
      completedAt: '2025-01-17T10:00:15Z',
      duration: 15000, // milliseconds
      result: 'Output da IA...',
      error: null,
    }
  ],
  total: 150,
  hasMore: true
}

getExecutionDetails

Obtém detalhes completos de uma execução.

const execution = await getExecutionDetails('exec_456');

Resposta Completa:

{
  id: 'exec_456',
  taskId: 'task_123',
  task: {
    id: 'task_123',
    name: 'Resumo Diário',
    prompt: 'Resuma...',
    model: 'google/gemini-2.5-pro'
  },
  status: 'SUCCESS',
  startedAt: '2025-01-17T10:00:00Z',
  completedAt: '2025-01-17T10:00:15Z',
  duration: 15000,
  result: 'Output completo da IA...',
  error: null,
  logs: [
    { timestamp: '...', message: 'Iniciando execução' },
    { timestamp: '...', message: 'Chamando OpenRouter API' },
    { timestamp: '...', message: 'Execução concluída' }
  ],
  metadata: {
    model: 'google/gemini-2.5-pro',
    tokens: { prompt: 100, completion: 500, total: 600 },
    cost: 0.002 // USD
  }
}

retryExecution

Tenta executar novamente uma tarefa que falhou.

const result = await retryExecution('exec_456');

User Actions

updateProfile

Atualiza perfil do usuário.

import { updateProfile } from '@/app/actions/user-actions';
 
const result = await updateProfile({
  name: 'Novo Nome',
  email: 'novo@email.com',
});

Schema:

const updateProfileSchema = z.object({
  name: z.string().min(2).max(100).optional(),
  email: z.string().email().optional(),
  timezone: z.string().optional(),
  notificationPreferences: z.object({
    email: z.boolean(),
    webhook: z.boolean(),
  }).optional(),
});

changePassword

Altera senha do usuário.

const result = await changePassword({
  currentPassword: 'senha123',
  newPassword: 'novaSenha456',
});

Validação:

const changePasswordSchema = z.object({
  currentPassword: z.string().min(6),
  newPassword: z.string()
    .min(8)
    .regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
      'Senha deve conter maiúscula, minúscula e número'
    ),
});

updateSettings

Atualiza configurações do usuário.

const result = await updateSettings({
  openRouterApiKey: 'sk-or-v1-...',
  webhookUrl: 'https://meu-webhook.com/endpoint',
  notificationEmails: ['email1@example.com', 'email2@example.com'],
});

Admin Actions

getSystemStats

Obtém estatísticas do sistema (apenas admin).

import { getSystemStats } from '@/app/actions/admin-actions';
 
const stats = await getSystemStats();

Resposta:

{
  users: {
    total: 1250,
    active: 980,
    new24h: 15
  },
  tasks: {
    total: 5600,
    active: 3200,
    paused: 2400
  },
  executions: {
    last24h: 12500,
    success: 11800,
    failed: 700,
    successRate: 94.4
  }
}

listUsers

Lista usuários (apenas admin).

const users = await listUsers({
  page: 1,
  limit: 50,
  search: 'email@example.com',
  role: 'USER',
});

updateUserRole

Altera role de um usuário (apenas admin).

const result = await updateUserRole('user_123', 'ADMIN');

createCoupon

Cria um novo cupom (apenas admin).

const coupon = await createCoupon({
  code: 'WELCOME30',
  type: 'TRIAL_EXTENSION',
  value: 30,
  maxUses: 100,
  expiresAt: new Date('2025-12-31'),
});

Webhook Actions

createWebhook

Registra um novo webhook.

import { createWebhook } from '@/app/actions/webhook-actions';
 
const webhook = await createWebhook({
  url: 'https://meu-webhook.com/endpoint',
  events: ['task.completed', 'task.failed'],
  secret: 'webhook_secret_123',
});

Eventos Disponíveis:

  • task.created
  • task.updated
  • task.deleted
  • task.completed
  • task.failed
  • execution.started
  • execution.completed
  • execution.failed

testWebhook

Envia um payload de teste para o webhook.

const result = await testWebhook('webhook_123');

Payload de Teste:

{
  "event": "test",
  "timestamp": "2025-01-17T10:00:00Z",
  "data": {
    "message": "This is a test webhook"
  }
}

Rate Limiting

Todas as actions têm rate limiting aplicado:

// Limites por usuário
const limits = {
  createTask: '10/minute',
  executeTask: '30/minute',
  updateTask: '20/minute',
  getExecutions: '100/minute',
};

Headers de Rate Limit

X-RateLimit-Limit: 10
X-RateLimit-Remaining: 8
X-RateLimit-Reset: 1674820800

Tratamento de Erros

Todas as actions retornam um padrão consistente:

Sucesso

{
  success: true,
  data: { /* resultado */ }
}

Erro de Validação

{
  success: false,
  error: 'Validation error',
  details: {
    name: ['Name must be at least 3 characters'],
    email: ['Invalid email format']
  }
}

Erro de Autenticação

{
  success: false,
  error: 'Unauthorized',
  code: 'AUTH_REQUIRED'
}

Erro de Permissão

{
  success: false,
  error: 'Forbidden',
  code: 'INSUFFICIENT_PERMISSIONS'
}

Erro de Rate Limit

{
  success: false,
  error: 'Too many requests',
  code: 'RATE_LIMIT_EXCEEDED',
  retryAfter: 60 // seconds
}

Exemplo Completo

'use client';
 
import { useState } from 'react';
import { createTask } from '@/app/actions/task-actions';
import { toast } from 'sonner';
 
export function CreateTaskForm() {
  const [loading, setLoading] = useState(false);
 
  const handleSubmit = async (formData: FormData) => {
    setLoading(true);
 
    try {
      const result = await createTask({
        name: formData.get('name') as string,
        prompt: formData.get('prompt') as string,
        model: formData.get('model') as string,
        recurrence: formData.get('recurrence') as string,
        active: true,
      });
 
      if (result.success) {
        toast.success('Tarefa criada com sucesso!');
        // Redirect ou atualizar UI
      } else {
        toast.error(result.error);
      }
    } catch (error) {
      toast.error('Erro ao criar tarefa');
    } finally {
      setLoading(false);
    }
  };
 
  return (
    <form action={handleSubmit}>
      {/* Campos do formulário */}
      <button type="submit" disabled={loading}>
        {loading ? 'Criando...' : 'Criar Tarefa'}
      </button>
    </form>
  );
}

Próximos Passos


Referência Completa: Veja o código-fonte em /src/app/actions/