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); // PausarexecuteTask
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.createdtask.updatedtask.deletedtask.completedtask.failedexecution.startedexecution.completedexecution.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: 1674820800Tratamento 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/