aumenta el tiempo de espera al fetchAllGroups hasta algo más de 5 minutos a ver si el problema es ese u otro

main
borja 3 months ago
parent ae8e3fad2b
commit 7c8143d9c3

@ -6,202 +6,202 @@ const env = process.env;
const activeGroupsCache = new Map<string, string>(); // groupId -> groupName const activeGroupsCache = new Map<string, string>(); // groupId -> groupName
type EvolutionGroup = { type EvolutionGroup = {
id: string; id: string;
subject: string; subject: string;
linkedParent?: string; linkedParent?: string;
// Other fields from API response // Other fields from API response
}; };
export class GroupSyncService { export class GroupSyncService {
/** /**
* Gets the sync interval duration in milliseconds. * Gets the sync interval duration in milliseconds.
* *
* Priority: * Priority:
* 1. GROUP_SYNC_INTERVAL_MS environment variable if set * 1. GROUP_SYNC_INTERVAL_MS environment variable if set
* 2. Default 24 hour interval * 2. Default 24 hour interval
* *
* In development mode, enforces minimum 10 second interval * In development mode, enforces minimum 10 second interval
* to prevent accidental excessive API calls. * to prevent accidental excessive API calls.
* *
* @returns {number} Sync interval in milliseconds * @returns {number} Sync interval in milliseconds
*/ */
private static get SYNC_INTERVAL_MS(): number { private static get SYNC_INTERVAL_MS(): number {
const interval = process.env.GROUP_SYNC_INTERVAL_MS const interval = process.env.GROUP_SYNC_INTERVAL_MS
? Number(process.env.GROUP_SYNC_INTERVAL_MS) ? Number(process.env.GROUP_SYNC_INTERVAL_MS)
: 24 * 60 * 60 * 1000; // Default 24 hours : 24 * 60 * 60 * 1000; // Default 24 hours
// Ensure minimum 10 second interval in development // Ensure minimum 10 second interval in development
if (process.env.NODE_ENV === 'development' && interval < 10000) { if (process.env.NODE_ENV === 'development' && interval < 10000) {
console.warn(`Sync interval too low (${interval}ms), using 10s minimum`); console.warn(`Sync interval too low (${interval}ms), using 10s minimum`);
return 10000; return 10000;
} }
return interval; return interval;
} }
private static lastSyncAttempt = 0; private static lastSyncAttempt = 0;
static async syncGroups(): Promise<{ added: number; updated: number }> { static async syncGroups(): Promise<{ added: number; updated: number }> {
if (!this.shouldSync()) { if (!this.shouldSync()) {
return { added: 0, updated: 0 }; return { added: 0, updated: 0 };
} }
try { try {
const communityId = env.WHATSAPP_COMMUNITY_ID; const communityId = env.WHATSAPP_COMMUNITY_ID;
if (!communityId) { if (!communityId) {
throw new Error('WHATSAPP_COMMUNITY_ID is not set'); throw new Error('WHATSAPP_COMMUNITY_ID is not set');
} }
const groups = await this.fetchGroupsFromAPI(); const groups = await this.fetchGroupsFromAPI();
const communityGroups = groups.filter( const communityGroups = groups.filter(
(group) => group.linkedParent === communityId (group) => group.linkedParent === communityId
); );
return await this.upsertGroups(communityGroups); return await this.upsertGroups(communityGroups);
} catch (error) { } catch (error) {
console.error('Group sync failed:', error); console.error('Group sync failed:', error);
throw error; throw error;
} finally { } finally {
this.lastSyncAttempt = Date.now(); this.lastSyncAttempt = Date.now();
} }
} }
private static shouldSync(): boolean { private static shouldSync(): boolean {
const timeSinceLastSync = Date.now() - this.lastSyncAttempt; const timeSinceLastSync = Date.now() - this.lastSyncAttempt;
const shouldSync = timeSinceLastSync > this.SYNC_INTERVAL_MS; const shouldSync = timeSinceLastSync > this.SYNC_INTERVAL_MS;
if (!shouldSync && process.env.NODE_ENV !== 'test') { if (!shouldSync && process.env.NODE_ENV !== 'test') {
const nextSyncIn = this.SYNC_INTERVAL_MS - timeSinceLastSync; const nextSyncIn = this.SYNC_INTERVAL_MS - timeSinceLastSync;
console.debug(`Next sync available in ${Math.round(nextSyncIn/1000)} seconds`); console.debug(`Next sync available in ${Math.round(nextSyncIn / 1000)} seconds`);
} }
return shouldSync; return shouldSync;
} }
private static async fetchGroupsFromAPI(): Promise<EvolutionGroup[]> { private static async fetchGroupsFromAPI(): Promise<EvolutionGroup[]> {
const url = `${env.EVOLUTION_API_URL}/group/fetchAllGroups/${env.EVOLUTION_API_INSTANCE}?getParticipants=false`; const url = `${env.EVOLUTION_API_URL}/group/fetchAllGroups/${env.EVOLUTION_API_INSTANCE}?getParticipants=false`;
console.log(' Fetching groups from API:', { console.log(' Fetching groups from API:', {
url: `${url.substring(0, 50)}...`, // Log partial URL for security url: `${url.substring(0, 50)}...`, // Log partial URL for security
communityId: env.WHATSAPP_COMMUNITY_ID, communityId: env.WHATSAPP_COMMUNITY_ID,
time: new Date().toISOString() time: new Date().toISOString()
}); });
try { try {
const response = await fetch(url, { const response = await fetch(url, {
headers: { headers: {
apikey: env.EVOLUTION_API_KEY, apikey: env.EVOLUTION_API_KEY,
}, },
timeout: 120000 // 120 second timeout timeout: 320000 // 120 second timeout
}); });
if (!response.ok) { if (!response.ok) {
const errorBody = await response.text().catch(() => 'Unable to read error body'); const errorBody = await response.text().catch(() => 'Unable to read error body');
console.error('❌ API request failed:', { console.error('❌ API request failed:', {
status: response.status, status: response.status,
statusText: response.statusText, statusText: response.statusText,
headers: Object.fromEntries(response.headers.entries()), headers: Object.fromEntries(response.headers.entries()),
body: errorBody body: errorBody
}); });
throw new Error(`API request failed: ${response.status} ${response.statusText}`); throw new Error(`API request failed: ${response.status} ${response.statusText}`);
} }
const data = await response.json(); const data = await response.json();
console.log(' API response:', { console.log(' API response:', {
status: data.status, status: data.status,
message: data.message, message: data.message,
responseLength: data.response?.length || 0 responseLength: data.response?.length || 0
}); });
if (data.status !== 'success') { if (data.status !== 'success') {
throw new Error(`API error: ${data.message || 'Unknown error'}`); throw new Error(`API error: ${data.message || 'Unknown error'}`);
} }
return data.response; return data.response;
} catch (error) { } catch (error) {
console.error('❌ Failed to fetch groups:', { console.error('❌ Failed to fetch groups:', {
error: error instanceof Error ? error.message : String(error), error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined stack: error instanceof Error ? error.stack : undefined
}); });
throw error; throw error;
} }
} }
private static cacheActiveGroups(): void { private static cacheActiveGroups(): void {
const groups = db.prepare('SELECT id, name FROM groups WHERE active = TRUE').all(); const groups = db.prepare('SELECT id, name FROM groups WHERE active = TRUE').all();
activeGroupsCache.clear(); activeGroupsCache.clear();
for (const group of groups) { for (const group of groups) {
activeGroupsCache.set(group.id, group.name); activeGroupsCache.set(group.id, group.name);
} }
console.log(`Cached ${activeGroupsCache.size} active groups`); console.log(`Cached ${activeGroupsCache.size} active groups`);
} }
private static getActiveGroupsCount(): number { private static getActiveGroupsCount(): number {
const result = db.prepare('SELECT COUNT(*) as count FROM groups WHERE active = TRUE').get(); const result = db.prepare('SELECT COUNT(*) as count FROM groups WHERE active = TRUE').get();
return result?.count || 0; return result?.count || 0;
} }
static async checkInitialGroups(): Promise<void> { static async checkInitialGroups(): Promise<void> {
const count = this.getActiveGroupsCount(); const count = this.getActiveGroupsCount();
if (count > 0) { if (count > 0) {
this.cacheActiveGroups(); this.cacheActiveGroups();
console.log(`✅ Using ${count} existing groups from database`); console.log(`✅ Using ${count} existing groups from database`);
return; return;
} }
console.log('⚠️ No groups found in database - performing initial sync'); console.log('⚠️ No groups found in database - performing initial sync');
try { try {
const { added } = await this.syncGroups(); const { added } = await this.syncGroups();
if (added === 0) { if (added === 0) {
throw new Error('Initial group sync completed but no groups were added'); throw new Error('Initial group sync completed but no groups were added');
} }
this.cacheActiveGroups(); this.cacheActiveGroups();
console.log(`✅ Initial group sync completed - added ${added} groups`); console.log(`✅ Initial group sync completed - added ${added} groups`);
} catch (error) { } catch (error) {
console.error('❌ Critical: Initial group sync failed - no groups available'); console.error('❌ Critical: Initial group sync failed - no groups available');
console.error(error instanceof Error ? error.message : 'Unknown error'); console.error(error instanceof Error ? error.message : 'Unknown error');
process.exit(1); process.exit(1);
} }
} }
private static async upsertGroups(groups: EvolutionGroup[]): Promise<{ added: number; updated: number }> { private static async upsertGroups(groups: EvolutionGroup[]): Promise<{ added: number; updated: number }> {
let added = 0; let added = 0;
let updated = 0; let updated = 0;
const transactionResult = db.transaction(() => { const transactionResult = db.transaction(() => {
// First mark all groups as inactive and update verification timestamp // First mark all groups as inactive and update verification timestamp
const inactiveResult = db.prepare(` const inactiveResult = db.prepare(`
UPDATE groups UPDATE groups
SET active = FALSE, SET active = FALSE,
last_verified = CURRENT_TIMESTAMP last_verified = CURRENT_TIMESTAMP
`).run(); `).run();
console.log('Marked groups inactive:', inactiveResult); console.log('Marked groups inactive:', inactiveResult);
for (const group of groups) { for (const group of groups) {
const existing = db.prepare('SELECT 1 FROM groups WHERE id = ?').get(group.id); const existing = db.prepare('SELECT 1 FROM groups WHERE id = ?').get(group.id);
console.log('Checking group:', group.id, 'exists:', !!existing); console.log('Checking group:', group.id, 'exists:', !!existing);
if (existing) { if (existing) {
const updateResult = db.prepare( const updateResult = db.prepare(
'UPDATE groups SET name = ?, active = TRUE, last_verified = CURRENT_TIMESTAMP WHERE id = ?' 'UPDATE groups SET name = ?, active = TRUE, last_verified = CURRENT_TIMESTAMP WHERE id = ?'
).run(group.subject, group.id); ).run(group.subject, group.id);
console.log('Updated group:', group.id, 'result:', updateResult); console.log('Updated group:', group.id, 'result:', updateResult);
updated++; updated++;
} else { } else {
const insertResult = db.prepare( const insertResult = db.prepare(
'INSERT INTO groups (id, community_id, name, active) VALUES (?, ?, ?, TRUE)' 'INSERT INTO groups (id, community_id, name, active) VALUES (?, ?, ?, TRUE)'
).run(group.id, env.WHATSAPP_COMMUNITY_ID, group.subject); ).run(group.id, env.WHATSAPP_COMMUNITY_ID, group.subject);
console.log('Added group:', group.id, 'result:', insertResult); console.log('Added group:', group.id, 'result:', insertResult);
added++; added++;
} }
} }
return { added, updated }; return { added, updated };
}); });
try { try {
const result = transactionResult(); const result = transactionResult();
console.log(`Group sync completed: ${result.added} added, ${result.updated} updated`); console.log(`Group sync completed: ${result.added} added, ${result.updated} updated`);
return result; return result;
} catch (error) { } catch (error) {
console.error('Error in upsertGroups:', error); console.error('Error in upsertGroups:', error);
throw error; throw error;
} }
} }
} }

Loading…
Cancel
Save