diff --git a/src/lib/ads-discovery.ts b/src/lib/ads-discovery.ts index 27585bc..41aa075 100644 --- a/src/lib/ads-discovery.ts +++ b/src/lib/ads-discovery.ts @@ -37,11 +37,13 @@ export async function resolveMetaAccessToken(): Promise { return tokens[0] || null; } -/** All candidate Meta tokens (env first — production 4V2030 system user). */ +/** + * Candidate Meta tokens. + * Prefer agency_oauth (system user with BM access to client ad accounts); + * Dokploy META_ACCESS_TOKEN may only reach a subset (historically BPS). + */ export async function resolveMetaAccessTokens(): Promise { const out: string[] = []; - const env = process.env.META_ACCESS_TOKEN?.trim(); - if (env) out.push(env); const headers = serviceHeaders(); if (headers) { @@ -53,12 +55,16 @@ export async function resolveMetaAccessTokens(): Promise { if (r.ok) { const rows = (await r.json()) as { access_token?: string }[]; const t = rows[0]?.access_token?.trim(); - if (t && !out.includes(t)) out.push(t); + if (t) out.push(t); } } catch { /* ignore */ } } + + const env = process.env.META_ACCESS_TOKEN?.trim(); + if (env && !out.includes(env)) out.push(env); + return out; } diff --git a/src/lib/ads-sync.ts b/src/lib/ads-sync.ts index ab635d3..bf82c60 100644 --- a/src/lib/ads-sync.ts +++ b/src/lib/ads-sync.ts @@ -1,7 +1,7 @@ /** Pull Meta / Google Ads insights into ad_daily + roll up kpi_weekly. */ import { serviceHeaders, supabaseUrl } from '@/lib/agency'; -import { resolveMetaAccessToken } from '@/lib/ads-discovery'; +import { resolveMetaAccessTokens } from '@/lib/ads-discovery'; import { leadFromMetaActions, linkClicksFromMetaActions, nMeta } from '@/lib/meta-leads'; import { resolveGoogleAdsAccessToken } from '@/lib/google-ads-auth'; @@ -174,18 +174,38 @@ async function fetchMetaAdDaily( tenantId: string, days: number ): Promise { - const token = await resolveMetaAccessToken(); - if (!token) throw new Error('Sin token Meta'); + const tokens = await resolveMetaAccessTokens(); + if (!tokens.length) throw new Error('Sin token Meta'); const act = accountId.startsWith('act_') ? accountId : `act_${accountId}`; // Meta rejects huge ad-level ranges — pull in ~30d windows const windows = dayWindows(days, 30); - const out: AdDailyInsert[] = []; - for (const w of windows) { - out.push( - ...(await fetchMetaInsightsWindow(act, token, tenantId, w.since, w.until)) - ); + const errors: string[] = []; + // Prefer agency_oauth (broader BM access); Dokploy env token may only cover BPS. + for (const token of tokens) { + try { + const out: AdDailyInsert[] = []; + for (const w of windows) { + out.push( + ...(await fetchMetaInsightsWindow( + act, + token, + tenantId, + w.since, + w.until + )) + ); + } + return out; + } catch (e) { + const msg = e instanceof Error ? e.message : String(e); + errors.push(msg); + // Try next token on permission / auth failures + if (!/#200|permission|OAuthException|ads_read|ads_management|invalid/i.test(msg)) { + throw e; + } + } } - return out; + throw new Error(errors[errors.length - 1] || 'Meta sync error'); } async function fetchGoogleWindow(