feat(creatives): task 5 - approval actions, outgoing webhooks with HMAC, and publish patch

This commit is contained in:
Marcos Fargas
2026-10-12 13:44:54 +02:00
parent 7b702cd4cc
commit 958c59a8ec
5 changed files with 608 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
import { NextRequest, NextResponse } from 'next/server';
import { authenticateIntegrationRequest } from '@/lib/integrations-auth';
import { createClient } from '@supabase/supabase-js';
export async function PATCH(
req: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
let body: any;
try {
body = await req.json();
} catch {
return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 });
}
const { id } = await params;
const auth = await authenticateIntegrationRequest(req, { requireTenant: true, parsedBody: body });
if (!auth.ok) {
return auth.errorResponse!;
}
const tenantId = auth.tenantId!;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY!;
const adminClient = createClient(supabaseUrl, serviceRoleKey);
// Obtener creatividad previa
const { data: creative, error: getErr } = await adminClient
.from('creatives')
.select('*')
.eq('id', id)
.eq('tenant_id', tenantId)
.maybeSingle();
if (getErr || !creative) {
return NextResponse.json({ error: 'Creatividad no encontrada' }, { status: 404 });
}
const prevStatus = creative.status;
const updates: Record<string, any> = {
updated_at: new Date().toISOString(),
};
if (body.meta_ad_id !== undefined) updates.meta_ad_id = body.meta_ad_id;
if (body.status !== undefined) updates.status = body.status;
if (body.adset_id !== undefined) updates.adset_id = body.adset_id;
if (body.campaign_name !== undefined) updates.campaign_name = body.campaign_name;
if (body.asset_url !== undefined) updates.asset_url = body.asset_url;
if (body.thumbnail_url !== undefined) updates.thumbnail_url = body.thumbnail_url;
const { data: updated, error: updateErr } = await adminClient
.from('creatives')
.update(updates)
.eq('id', id)
.eq('tenant_id', tenantId)
.select('*')
.single();
if (updateErr) {
return NextResponse.json({ error: updateErr.message }, { status: 500 });
}
// Si pasa a publicada o cambia meta_ad_id, registrar en creative_events
if (updates.status === 'publicada' || updates.meta_ad_id) {
await adminClient.from('creative_events').insert({
tenant_id: tenantId,
creative_id: id,
event_type: updates.status === 'publicada' ? 'published' : 'creative_updated',
actor: auth.caller || 'n8n',
from_status: prevStatus,
to_status: updates.status || prevStatus,
payload: {
meta_ad_id: updates.meta_ad_id || creative.meta_ad_id,
adset_id: updates.adset_id,
},
visibility: 'client',
});
}
return NextResponse.json(updated);
}