feat(creatives): task 4 - claude integration endpoints (generation-requests, creatives, context, events)
This commit is contained in:
134
src/app/api/integrations/creatives/route.ts
Normal file
134
src/app/api/integrations/creatives/route.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { authenticateIntegrationRequest } from '@/lib/integrations-auth';
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
let body: any;
|
||||
try {
|
||||
body = await req.json();
|
||||
} catch {
|
||||
return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 });
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// Acepta array o un único objeto
|
||||
const items = Array.isArray(body)
|
||||
? body
|
||||
: Array.isArray(body.creatives)
|
||||
? body.creatives
|
||||
: [body];
|
||||
|
||||
if (!items.length) {
|
||||
return NextResponse.json({ error: 'No creatives provided' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Si viene generation_request_id, averiguar parent_id (source_creative_id del request)
|
||||
let defaultParentId: string | null = null;
|
||||
const genRequestId = body.generation_request_id || items[0]?.generation_request_id;
|
||||
|
||||
if (genRequestId) {
|
||||
const { data: genReq } = await adminClient
|
||||
.from('generation_requests')
|
||||
.select('source_creative_id')
|
||||
.eq('id', genRequestId)
|
||||
.eq('tenant_id', tenantId)
|
||||
.maybeSingle();
|
||||
|
||||
if (genReq) {
|
||||
defaultParentId = genReq.source_creative_id;
|
||||
}
|
||||
}
|
||||
|
||||
const toInsert = items.map((c: any) => ({
|
||||
tenant_id: tenantId,
|
||||
campaign_name: c.campaign_name || null,
|
||||
adset_id: c.adset_id || null,
|
||||
meta_ad_id: c.meta_ad_id || null,
|
||||
format: c.format || 'estatico',
|
||||
angle: c.angle || null,
|
||||
hook: c.hook || null,
|
||||
primary_text: c.primary_text || null,
|
||||
headline: c.headline || null,
|
||||
cta: c.cta || null,
|
||||
asset_url: c.asset_url || null,
|
||||
thumbnail_url: c.thumbnail_url || null,
|
||||
status: 'borrador', // nacen siempre en borrador
|
||||
visibility: 'agency', // nacen siempre en agency
|
||||
created_by: 'claude', // creadas por Claude
|
||||
parent_id: c.parent_id || defaultParentId,
|
||||
generation_request_id: c.generation_request_id || genRequestId || null,
|
||||
}));
|
||||
|
||||
const { data: inserted, error } = await adminClient
|
||||
.from('creatives')
|
||||
.insert(toInsert)
|
||||
.select('*');
|
||||
|
||||
if (error) {
|
||||
return NextResponse.json({ error: error.message }, { status: 500 });
|
||||
}
|
||||
|
||||
// Registrar cada creación en creative_events con actor 'claude'
|
||||
for (const c of inserted || []) {
|
||||
await adminClient.from('creative_events').insert({
|
||||
tenant_id: tenantId,
|
||||
creative_id: c.id,
|
||||
event_type: 'creative_generated',
|
||||
actor: 'claude',
|
||||
to_status: 'borrador',
|
||||
payload: {
|
||||
angle: c.angle,
|
||||
format: c.format,
|
||||
generation_request_id: c.generation_request_id,
|
||||
parent_id: c.parent_id,
|
||||
},
|
||||
visibility: 'agency',
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
count: inserted?.length || 0,
|
||||
creatives: inserted,
|
||||
});
|
||||
}
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const auth = await authenticateIntegrationRequest(req, { requireTenant: true });
|
||||
if (!auth.ok) {
|
||||
return auth.errorResponse!;
|
||||
}
|
||||
|
||||
const tenantId = auth.tenantId!;
|
||||
const status = req.nextUrl.searchParams.get('status');
|
||||
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
|
||||
const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY!;
|
||||
const adminClient = createClient(supabaseUrl, serviceRoleKey);
|
||||
|
||||
let query = adminClient
|
||||
.from('creatives')
|
||||
.select('*')
|
||||
.eq('tenant_id', tenantId);
|
||||
|
||||
if (status) {
|
||||
query = query.eq('status', status);
|
||||
}
|
||||
|
||||
const { data: creatives, error } = await query;
|
||||
|
||||
if (error) {
|
||||
return NextResponse.json({ error: error.message }, { status: 500 });
|
||||
}
|
||||
|
||||
return NextResponse.json(creatives || []);
|
||||
}
|
||||
Reference in New Issue
Block a user