fix: Meta sync usa token agency_oauth (no solo env BPS)
El META_ACCESS_TOKEN de Dokploy solo tenía acceso a BPS; el de agency_oauth cubre el resto. Probamos candidatos y priorizamos agency. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -37,11 +37,13 @@ export async function resolveMetaAccessToken(): Promise<string | null> {
|
|||||||
return tokens[0] || null;
|
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<string[]> {
|
export async function resolveMetaAccessTokens(): Promise<string[]> {
|
||||||
const out: string[] = [];
|
const out: string[] = [];
|
||||||
const env = process.env.META_ACCESS_TOKEN?.trim();
|
|
||||||
if (env) out.push(env);
|
|
||||||
|
|
||||||
const headers = serviceHeaders();
|
const headers = serviceHeaders();
|
||||||
if (headers) {
|
if (headers) {
|
||||||
@@ -53,12 +55,16 @@ export async function resolveMetaAccessTokens(): Promise<string[]> {
|
|||||||
if (r.ok) {
|
if (r.ok) {
|
||||||
const rows = (await r.json()) as { access_token?: string }[];
|
const rows = (await r.json()) as { access_token?: string }[];
|
||||||
const t = rows[0]?.access_token?.trim();
|
const t = rows[0]?.access_token?.trim();
|
||||||
if (t && !out.includes(t)) out.push(t);
|
if (t) out.push(t);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
/* ignore */
|
/* ignore */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const env = process.env.META_ACCESS_TOKEN?.trim();
|
||||||
|
if (env && !out.includes(env)) out.push(env);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/** Pull Meta / Google Ads insights into ad_daily + roll up kpi_weekly. */
|
/** Pull Meta / Google Ads insights into ad_daily + roll up kpi_weekly. */
|
||||||
|
|
||||||
import { serviceHeaders, supabaseUrl } from '@/lib/agency';
|
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 { leadFromMetaActions, linkClicksFromMetaActions, nMeta } from '@/lib/meta-leads';
|
||||||
import { resolveGoogleAdsAccessToken } from '@/lib/google-ads-auth';
|
import { resolveGoogleAdsAccessToken } from '@/lib/google-ads-auth';
|
||||||
|
|
||||||
@@ -174,18 +174,38 @@ async function fetchMetaAdDaily(
|
|||||||
tenantId: string,
|
tenantId: string,
|
||||||
days: number
|
days: number
|
||||||
): Promise<AdDailyInsert[]> {
|
): Promise<AdDailyInsert[]> {
|
||||||
const token = await resolveMetaAccessToken();
|
const tokens = await resolveMetaAccessTokens();
|
||||||
if (!token) throw new Error('Sin token Meta');
|
if (!tokens.length) throw new Error('Sin token Meta');
|
||||||
const act = accountId.startsWith('act_') ? accountId : `act_${accountId}`;
|
const act = accountId.startsWith('act_') ? accountId : `act_${accountId}`;
|
||||||
// Meta rejects huge ad-level ranges — pull in ~30d windows
|
// Meta rejects huge ad-level ranges — pull in ~30d windows
|
||||||
const windows = dayWindows(days, 30);
|
const windows = dayWindows(days, 30);
|
||||||
|
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[] = [];
|
const out: AdDailyInsert[] = [];
|
||||||
for (const w of windows) {
|
for (const w of windows) {
|
||||||
out.push(
|
out.push(
|
||||||
...(await fetchMetaInsightsWindow(act, token, tenantId, w.since, w.until))
|
...(await fetchMetaInsightsWindow(
|
||||||
|
act,
|
||||||
|
token,
|
||||||
|
tenantId,
|
||||||
|
w.since,
|
||||||
|
w.until
|
||||||
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return out;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(errors[errors.length - 1] || 'Meta sync error');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchGoogleWindow(
|
async function fetchGoogleWindow(
|
||||||
|
|||||||
Reference in New Issue
Block a user