2026-06-22 15:26:20 -03:00
|
|
|
import { Injectable } from '@nestjs/common';
|
|
|
|
|
import { WhatsappConfigRepository } from './repositories/whatsapp-config.repository';
|
|
|
|
|
|
|
|
|
|
interface WhatsappConfig {
|
|
|
|
|
access_token: string | null;
|
|
|
|
|
phone_number_id: string | null;
|
|
|
|
|
api_version: string;
|
|
|
|
|
webhook_token: string | null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
|
export class WhatsappConfigService {
|
|
|
|
|
private _cache: { config: WhatsappConfig; expiresAt: number } | null = null;
|
|
|
|
|
private readonly CACHE_TTL_MS = 60_000;
|
|
|
|
|
|
|
|
|
|
constructor(private readonly repo: WhatsappConfigRepository) {}
|
|
|
|
|
|
|
|
|
|
async getConfig(): Promise<WhatsappConfig> {
|
|
|
|
|
if (this._cache && Date.now() < this._cache.expiresAt) {
|
|
|
|
|
return this._cache.config;
|
|
|
|
|
}
|
|
|
|
|
const row = await this.repo.find();
|
|
|
|
|
const config: WhatsappConfig = {
|
|
|
|
|
access_token: row?.access_token || process.env.META_ACCESS_TOKEN || null,
|
|
|
|
|
phone_number_id: row?.phone_number_id || process.env.META_PHONE_NUMBER_ID || null,
|
|
|
|
|
api_version: row?.api_version || process.env.META_API_VERSION || 'v25.0',
|
|
|
|
|
webhook_token: row?.webhook_token || process.env.META_WEBHOOK_TOKEN || null,
|
|
|
|
|
};
|
|
|
|
|
this._cache = { config, expiresAt: Date.now() + this.CACHE_TTL_MS };
|
|
|
|
|
return config;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getPublicConfig() {
|
|
|
|
|
const row = await this.repo.find();
|
|
|
|
|
const effectiveToken = row?.access_token || process.env.META_ACCESS_TOKEN;
|
|
|
|
|
const effectivePhoneId = row?.phone_number_id || process.env.META_PHONE_NUMBER_ID;
|
|
|
|
|
return {
|
|
|
|
|
phone_number_id: effectivePhoneId || null,
|
|
|
|
|
api_version: row?.api_version || process.env.META_API_VERSION || 'v25.0',
|
|
|
|
|
webhook_token: row?.webhook_token || process.env.META_WEBHOOK_TOKEN || null,
|
|
|
|
|
is_configured: !!(effectiveToken && effectivePhoneId),
|
|
|
|
|
has_token: !!effectiveToken,
|
|
|
|
|
token_source: row?.access_token ? 'database' : effectiveToken ? 'env' : null,
|
|
|
|
|
updated_at: row?.updated_at || null,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async saveConfig(
|
|
|
|
|
dto: { access_token?: string; phone_number_id?: string; api_version?: string; webhook_token?: string },
|
|
|
|
|
userId?: number,
|
|
|
|
|
) {
|
|
|
|
|
this._cache = null;
|
|
|
|
|
await this.repo.update({
|
|
|
|
|
access_token: dto.access_token || null,
|
|
|
|
|
phone_number_id: dto.phone_number_id || null,
|
|
|
|
|
api_version: dto.api_version || null,
|
|
|
|
|
webhook_token: dto.webhook_token || null,
|
|
|
|
|
updated_by: userId,
|
|
|
|
|
});
|
|
|
|
|
return this.getPublicConfig();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async validateCredentials(token: string, phoneNumberId: string, apiVersion = 'v25.0') {
|
|
|
|
|
const url = `https://graph.facebook.com/${apiVersion}/${phoneNumberId}?access_token=${token}&fields=display_phone_number,verified_name,quality_rating`;
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(url);
|
|
|
|
|
const data = (await response.json()) as any;
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
return {
|
|
|
|
|
valid: false,
|
|
|
|
|
error: data?.error?.message || 'Credenciais inválidas',
|
|
|
|
|
code: data?.error?.code,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
valid: true,
|
|
|
|
|
phone_number: data.display_phone_number,
|
|
|
|
|
verified_name: data.verified_name,
|
|
|
|
|
quality_rating: data.quality_rating,
|
|
|
|
|
};
|
2026-06-22 15:36:09 -03:00
|
|
|
} catch (err: any) {
|
|
|
|
|
const msg = err?.message || String(err);
|
|
|
|
|
console.error('[WhatsappConfig] validateCredentials falhou:', msg);
|
|
|
|
|
return { valid: false, error: `Erro de conexão com a Meta API: ${msg}` };
|
2026-06-22 15:26:20 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|