feat: centralizar ADN y estilo de marca en Zona de Marca y simplificar panel de creatividades a operativa ads
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { authenticateIntegrationRequest } from '@/lib/integrations-auth';
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
import { compileClientSkillMarkdown, normalizeCreativeStyle } from '@/lib/creative-style';
|
||||
import { normalizeCreativeStyle } from '@/lib/creative-style';
|
||||
import {
|
||||
compileClientSkillFromBrandbook,
|
||||
parseBrandbook,
|
||||
seedBrandbook,
|
||||
type Brandbook,
|
||||
} from '@/lib/brandbook';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
@@ -32,36 +38,54 @@ export async function GET(req: NextRequest) {
|
||||
const slug = (tenant.slug || 'bps').toLowerCase().trim();
|
||||
const formatParam = (req.nextUrl.searchParams.get('format') || 'estatico').toLowerCase().trim();
|
||||
|
||||
// 2. Resolver skill del cliente: primero desde BD (creative_style editable en la app), fallback a archivo en disco
|
||||
// 2. Obtener brandbook de documents (fuente central de verdad: Zona de Marca)
|
||||
const { data: brandDoc } = await adminClient
|
||||
.from('documents')
|
||||
.select('content')
|
||||
.eq('tenant_id', tenantId)
|
||||
.eq('type', 'brandbook')
|
||||
.order('updated_at', { ascending: false })
|
||||
.limit(1)
|
||||
.maybeSingle();
|
||||
|
||||
let brandbook: Brandbook | null = null;
|
||||
if (brandDoc?.content) {
|
||||
try {
|
||||
const parsedContent =
|
||||
typeof brandDoc.content === 'string'
|
||||
? JSON.parse(brandDoc.content)
|
||||
: brandDoc.content;
|
||||
brandbook = parseBrandbook(parsedContent);
|
||||
} catch {
|
||||
brandbook = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!brandbook) {
|
||||
brandbook = seedBrandbook(slug, tenant.name || 'Cliente');
|
||||
}
|
||||
|
||||
// 3. Obtener configuración operativa de Ads (Freepik Mystic, Canva y Reglas de campaña)
|
||||
const rawDbStyle =
|
||||
tenant.creative_style && Object.keys(tenant.creative_style).length > 0
|
||||
? tenant.creative_style
|
||||
: (tenant.settings as any)?.creative_style;
|
||||
|
||||
let clientSkill = '';
|
||||
const operativeStyle = normalizeCreativeStyle(rawDbStyle, tenant.name || 'Cliente', slug);
|
||||
|
||||
// 4. Compilar skill de cliente a partir de Zona de Marca + Operativa Ads
|
||||
let clientSkill = compileClientSkillFromBrandbook(
|
||||
brandbook,
|
||||
{
|
||||
image_ai: operativeStyle.image_ai,
|
||||
campaign_rules: operativeStyle.campaign_rules,
|
||||
},
|
||||
tenant.name || 'Cliente',
|
||||
slug
|
||||
);
|
||||
|
||||
// 5. Resolver skill del formato (skills/formats/<format>-4-5.md o skills/formats/<format>.md, fallback a estatico)
|
||||
const skillsDir = path.join(process.cwd(), 'skills');
|
||||
|
||||
if (rawDbStyle && Object.keys(rawDbStyle).length > 0) {
|
||||
const normalized = normalizeCreativeStyle(rawDbStyle, tenant.name || 'Cliente', slug);
|
||||
clientSkill = compileClientSkillMarkdown(normalized, tenant.name || 'Cliente', slug);
|
||||
} else {
|
||||
const clientCandidates = [
|
||||
path.join(skillsDir, 'clients', `${slug}.md`),
|
||||
path.join(skillsDir, 'clients', slug === 'bps' ? 'blue-parrot-school.md' : `${slug}.md`),
|
||||
];
|
||||
for (const p of clientCandidates) {
|
||||
if (fs.existsSync(p)) {
|
||||
clientSkill = fs.readFileSync(p, 'utf-8');
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!clientSkill) {
|
||||
const normalized = normalizeCreativeStyle(null, tenant.name || 'Cliente', slug);
|
||||
clientSkill = compileClientSkillMarkdown(normalized, tenant.name || 'Cliente', slug);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Resolver skill del formato (skills/formats/<format>-4-5.md o skills/formats/<format>.md, fallback a estatico)
|
||||
const formatCandidates = [
|
||||
path.join(skillsDir, 'formats', `${formatParam}-4-5.md`),
|
||||
path.join(skillsDir, 'formats', `${formatParam}.md`),
|
||||
|
||||
Reference in New Issue
Block a user