import { NextRequest, NextResponse } from 'next/server'; import { createClient } from '@/lib/supabase/server'; import { isAgencyRole, serviceHeaders, supabaseUrl, userRole, } from '@/lib/agency'; import type { TenantSettings } from '@/lib/tenant-modules'; export const dynamic = 'force-dynamic'; async function requireAgency() { const supabase = await createClient(); const { data: { user }, } = await supabase.auth.getUser(); if (!user) { return { error: NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) }; } if (!isAgencyRole(userRole(user))) { return { error: NextResponse.json({ error: 'Forbidden' }, { status: 403 }) }; } const headers = serviceHeaders(); if (!headers) { return { error: NextResponse.json({ error: 'no service role' }, { status: 503 }), }; } return { headers }; } /** GET ?tenantId= — full ficha payload */ export async function GET(req: NextRequest) { const gate = await requireAgency(); if ('error' in gate && gate.error) return gate.error; const { headers } = gate as { headers: { apikey: string; Authorization: string }; }; const tenantId = req.nextUrl.searchParams.get('tenantId')?.trim() || ''; if (!tenantId) { return NextResponse.json({ error: 'tenantId requerido' }, { status: 400 }); } const base = supabaseUrl(); const [tRes, iRes] = await Promise.all([ fetch( `${base}/rest/v1/tenants?id=eq.${encodeURIComponent(tenantId)}&select=id,name,slug,logo_url,settings,agency_followed,agency_notes,created_at`, { headers, cache: 'no-store' } ), fetch( `${base}/rest/v1/integrations?tenant_id=eq.${encodeURIComponent(tenantId)}&select=provider,status,enabled,external_account_id,label,last_sync_at,error_message,meta`, { headers, cache: 'no-store' } ), ]); if (!tRes.ok) { const msg = await tRes.text(); return NextResponse.json( { error: msg.includes('logo_url') || msg.includes('settings') ? 'Aplicar sql/tenant_ficha.sql' : msg.slice(0, 300), }, { status: 500 } ); } const tenants = await tRes.json(); const tenant = tenants[0]; if (!tenant) { return NextResponse.json({ error: 'Cliente no encontrado' }, { status: 404 }); } let integrations: unknown[] = []; let integWarning: string | null = null; if (iRes.ok) { integrations = await iRes.json(); } else { integWarning = (await iRes.text()).slice(0, 200); } return NextResponse.json({ tenant: { id: tenant.id, name: tenant.name, slug: tenant.slug, logoUrl: tenant.logo_url || null, settings: (tenant.settings || {}) as TenantSettings, followed: tenant.agency_followed !== false, notes: tenant.agency_notes || '', createdAt: tenant.created_at, }, integrations, warning: integWarning, }); } /** PATCH — follow/notes + ficha fields (name, logoUrl, settings). */ export async function PATCH(req: NextRequest) { const gate = await requireAgency(); if ('error' in gate && gate.error) return gate.error; const { headers } = gate as { headers: { apikey: string; Authorization: string }; }; const body = await req.json().catch(() => ({})); const tenantId = typeof body.tenantId === 'string' ? body.tenantId.trim() : ''; if (!tenantId) { return NextResponse.json({ error: 'tenantId requerido' }, { status: 400 }); } const patch: Record = {}; if (typeof body.followed === 'boolean') { patch.agency_followed = body.followed; } if (typeof body.notes === 'string') { patch.agency_notes = body.notes.trim() || null; } if (typeof body.name === 'string' && body.name.trim()) { patch.name = body.name.trim(); } if (typeof body.logoUrl === 'string') { patch.logo_url = body.logoUrl.trim() || null; } if (body.logoUrl === null) { patch.logo_url = null; } if (body.settings && typeof body.settings === 'object') { patch.settings = body.settings as TenantSettings; } if (!Object.keys(patch).length) { return NextResponse.json( { error: 'Nada que actualizar' }, { status: 400 } ); } const r = await fetch( `${supabaseUrl()}/rest/v1/tenants?id=eq.${encodeURIComponent(tenantId)}`, { method: 'PATCH', headers: { ...headers, 'Content-Type': 'application/json', Prefer: 'return=representation', }, body: JSON.stringify(patch), } ); if (!r.ok) { const msg = await r.text(); return NextResponse.json( { error: msg.includes('logo_url') || msg.includes('settings') ? 'Aplicar sql/tenant_ficha.sql en Supabase' : msg.includes('agency_followed') ? 'Aplicar sql/agency_control.sql' : msg.slice(0, 300), }, { status: 500 } ); } const rows = await r.json(); return NextResponse.json({ tenant: rows[0] || rows }); }