From 7c8143d9c3d24a88d1a2817345c9502515f790a7 Mon Sep 17 00:00:00 2001 From: borja Date: Sun, 30 Mar 2025 14:26:17 +0200 Subject: [PATCH] =?UTF-8?q?aumenta=20el=20tiempo=20de=20espera=20al=20fetc?= =?UTF-8?q?hAllGroups=20hasta=20algo=20m=C3=A1s=20de=205=20minutos=20a=20v?= =?UTF-8?q?er=20si=20el=20problema=20es=20ese=20u=20otro?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/group-sync.ts | 382 ++++++++++++++++++------------------- 1 file changed, 191 insertions(+), 191 deletions(-) diff --git a/src/services/group-sync.ts b/src/services/group-sync.ts index 0b65d81..f48395b 100644 --- a/src/services/group-sync.ts +++ b/src/services/group-sync.ts @@ -6,202 +6,202 @@ const env = process.env; const activeGroupsCache = new Map(); // groupId -> groupName type EvolutionGroup = { - id: string; - subject: string; - linkedParent?: string; - // Other fields from API response + id: string; + subject: string; + linkedParent?: string; + // Other fields from API response }; export class GroupSyncService { - /** - * Gets the sync interval duration in milliseconds. - * - * Priority: - * 1. GROUP_SYNC_INTERVAL_MS environment variable if set - * 2. Default 24 hour interval - * - * In development mode, enforces minimum 10 second interval - * to prevent accidental excessive API calls. - * - * @returns {number} Sync interval in milliseconds - */ - private static get SYNC_INTERVAL_MS(): number { - const interval = process.env.GROUP_SYNC_INTERVAL_MS - ? Number(process.env.GROUP_SYNC_INTERVAL_MS) - : 24 * 60 * 60 * 1000; // Default 24 hours - - // Ensure minimum 10 second interval in development - if (process.env.NODE_ENV === 'development' && interval < 10000) { - console.warn(`Sync interval too low (${interval}ms), using 10s minimum`); - return 10000; - } - return interval; - } - private static lastSyncAttempt = 0; - - static async syncGroups(): Promise<{ added: number; updated: number }> { - if (!this.shouldSync()) { - return { added: 0, updated: 0 }; - } - - try { - const communityId = env.WHATSAPP_COMMUNITY_ID; - if (!communityId) { - throw new Error('WHATSAPP_COMMUNITY_ID is not set'); - } - - const groups = await this.fetchGroupsFromAPI(); - const communityGroups = groups.filter( - (group) => group.linkedParent === communityId - ); - - return await this.upsertGroups(communityGroups); - } catch (error) { - console.error('Group sync failed:', error); - throw error; - } finally { - this.lastSyncAttempt = Date.now(); - } - } - - private static shouldSync(): boolean { - const timeSinceLastSync = Date.now() - this.lastSyncAttempt; - const shouldSync = timeSinceLastSync > this.SYNC_INTERVAL_MS; - - if (!shouldSync && process.env.NODE_ENV !== 'test') { - const nextSyncIn = this.SYNC_INTERVAL_MS - timeSinceLastSync; - console.debug(`Next sync available in ${Math.round(nextSyncIn/1000)} seconds`); - } - - return shouldSync; - } - - private static async fetchGroupsFromAPI(): Promise { - const url = `${env.EVOLUTION_API_URL}/group/fetchAllGroups/${env.EVOLUTION_API_INSTANCE}?getParticipants=false`; - console.log('ℹ️ Fetching groups from API:', { - url: `${url.substring(0, 50)}...`, // Log partial URL for security - communityId: env.WHATSAPP_COMMUNITY_ID, - time: new Date().toISOString() - }); - - try { - const response = await fetch(url, { - headers: { - apikey: env.EVOLUTION_API_KEY, - }, - timeout: 120000 // 120 second timeout - }); - - if (!response.ok) { - const errorBody = await response.text().catch(() => 'Unable to read error body'); - console.error('❌ API request failed:', { - status: response.status, - statusText: response.statusText, - headers: Object.fromEntries(response.headers.entries()), - body: errorBody - }); - throw new Error(`API request failed: ${response.status} ${response.statusText}`); - } - - const data = await response.json(); - console.log('ℹ️ API response:', { - status: data.status, - message: data.message, - responseLength: data.response?.length || 0 - }); - - if (data.status !== 'success') { - throw new Error(`API error: ${data.message || 'Unknown error'}`); - } - return data.response; - } catch (error) { - console.error('❌ Failed to fetch groups:', { - error: error instanceof Error ? error.message : String(error), - stack: error instanceof Error ? error.stack : undefined - }); - throw error; - } - } - - private static cacheActiveGroups(): void { - const groups = db.prepare('SELECT id, name FROM groups WHERE active = TRUE').all(); - activeGroupsCache.clear(); - for (const group of groups) { - activeGroupsCache.set(group.id, group.name); - } - console.log(`Cached ${activeGroupsCache.size} active groups`); - } - - private static getActiveGroupsCount(): number { - const result = db.prepare('SELECT COUNT(*) as count FROM groups WHERE active = TRUE').get(); - return result?.count || 0; - } - - static async checkInitialGroups(): Promise { - const count = this.getActiveGroupsCount(); - if (count > 0) { - this.cacheActiveGroups(); - console.log(`✅ Using ${count} existing groups from database`); - return; - } - - console.log('⚠️ No groups found in database - performing initial sync'); - try { - const { added } = await this.syncGroups(); - if (added === 0) { - throw new Error('Initial group sync completed but no groups were added'); - } - this.cacheActiveGroups(); - console.log(`✅ Initial group sync completed - added ${added} groups`); - } catch (error) { - console.error('❌ Critical: Initial group sync failed - no groups available'); - console.error(error instanceof Error ? error.message : 'Unknown error'); - process.exit(1); - } - } - - private static async upsertGroups(groups: EvolutionGroup[]): Promise<{ added: number; updated: number }> { - let added = 0; - let updated = 0; - - const transactionResult = db.transaction(() => { - // First mark all groups as inactive and update verification timestamp - const inactiveResult = db.prepare(` + /** + * Gets the sync interval duration in milliseconds. + * + * Priority: + * 1. GROUP_SYNC_INTERVAL_MS environment variable if set + * 2. Default 24 hour interval + * + * In development mode, enforces minimum 10 second interval + * to prevent accidental excessive API calls. + * + * @returns {number} Sync interval in milliseconds + */ + private static get SYNC_INTERVAL_MS(): number { + const interval = process.env.GROUP_SYNC_INTERVAL_MS + ? Number(process.env.GROUP_SYNC_INTERVAL_MS) + : 24 * 60 * 60 * 1000; // Default 24 hours + + // Ensure minimum 10 second interval in development + if (process.env.NODE_ENV === 'development' && interval < 10000) { + console.warn(`Sync interval too low (${interval}ms), using 10s minimum`); + return 10000; + } + return interval; + } + private static lastSyncAttempt = 0; + + static async syncGroups(): Promise<{ added: number; updated: number }> { + if (!this.shouldSync()) { + return { added: 0, updated: 0 }; + } + + try { + const communityId = env.WHATSAPP_COMMUNITY_ID; + if (!communityId) { + throw new Error('WHATSAPP_COMMUNITY_ID is not set'); + } + + const groups = await this.fetchGroupsFromAPI(); + const communityGroups = groups.filter( + (group) => group.linkedParent === communityId + ); + + return await this.upsertGroups(communityGroups); + } catch (error) { + console.error('Group sync failed:', error); + throw error; + } finally { + this.lastSyncAttempt = Date.now(); + } + } + + private static shouldSync(): boolean { + const timeSinceLastSync = Date.now() - this.lastSyncAttempt; + const shouldSync = timeSinceLastSync > this.SYNC_INTERVAL_MS; + + if (!shouldSync && process.env.NODE_ENV !== 'test') { + const nextSyncIn = this.SYNC_INTERVAL_MS - timeSinceLastSync; + console.debug(`Next sync available in ${Math.round(nextSyncIn / 1000)} seconds`); + } + + return shouldSync; + } + + private static async fetchGroupsFromAPI(): Promise { + const url = `${env.EVOLUTION_API_URL}/group/fetchAllGroups/${env.EVOLUTION_API_INSTANCE}?getParticipants=false`; + console.log('ℹ️ Fetching groups from API:', { + url: `${url.substring(0, 50)}...`, // Log partial URL for security + communityId: env.WHATSAPP_COMMUNITY_ID, + time: new Date().toISOString() + }); + + try { + const response = await fetch(url, { + headers: { + apikey: env.EVOLUTION_API_KEY, + }, + timeout: 320000 // 120 second timeout + }); + + if (!response.ok) { + const errorBody = await response.text().catch(() => 'Unable to read error body'); + console.error('❌ API request failed:', { + status: response.status, + statusText: response.statusText, + headers: Object.fromEntries(response.headers.entries()), + body: errorBody + }); + throw new Error(`API request failed: ${response.status} ${response.statusText}`); + } + + const data = await response.json(); + console.log('ℹ️ API response:', { + status: data.status, + message: data.message, + responseLength: data.response?.length || 0 + }); + + if (data.status !== 'success') { + throw new Error(`API error: ${data.message || 'Unknown error'}`); + } + return data.response; + } catch (error) { + console.error('❌ Failed to fetch groups:', { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined + }); + throw error; + } + } + + private static cacheActiveGroups(): void { + const groups = db.prepare('SELECT id, name FROM groups WHERE active = TRUE').all(); + activeGroupsCache.clear(); + for (const group of groups) { + activeGroupsCache.set(group.id, group.name); + } + console.log(`Cached ${activeGroupsCache.size} active groups`); + } + + private static getActiveGroupsCount(): number { + const result = db.prepare('SELECT COUNT(*) as count FROM groups WHERE active = TRUE').get(); + return result?.count || 0; + } + + static async checkInitialGroups(): Promise { + const count = this.getActiveGroupsCount(); + if (count > 0) { + this.cacheActiveGroups(); + console.log(`✅ Using ${count} existing groups from database`); + return; + } + + console.log('⚠️ No groups found in database - performing initial sync'); + try { + const { added } = await this.syncGroups(); + if (added === 0) { + throw new Error('Initial group sync completed but no groups were added'); + } + this.cacheActiveGroups(); + console.log(`✅ Initial group sync completed - added ${added} groups`); + } catch (error) { + console.error('❌ Critical: Initial group sync failed - no groups available'); + console.error(error instanceof Error ? error.message : 'Unknown error'); + process.exit(1); + } + } + + private static async upsertGroups(groups: EvolutionGroup[]): Promise<{ added: number; updated: number }> { + let added = 0; + let updated = 0; + + const transactionResult = db.transaction(() => { + // First mark all groups as inactive and update verification timestamp + const inactiveResult = db.prepare(` UPDATE groups SET active = FALSE, last_verified = CURRENT_TIMESTAMP `).run(); - console.log('Marked groups inactive:', inactiveResult); - - for (const group of groups) { - const existing = db.prepare('SELECT 1 FROM groups WHERE id = ?').get(group.id); - console.log('Checking group:', group.id, 'exists:', !!existing); - - if (existing) { - const updateResult = db.prepare( - 'UPDATE groups SET name = ?, active = TRUE, last_verified = CURRENT_TIMESTAMP WHERE id = ?' - ).run(group.subject, group.id); - console.log('Updated group:', group.id, 'result:', updateResult); - updated++; - } else { - const insertResult = db.prepare( - 'INSERT INTO groups (id, community_id, name, active) VALUES (?, ?, ?, TRUE)' - ).run(group.id, env.WHATSAPP_COMMUNITY_ID, group.subject); - console.log('Added group:', group.id, 'result:', insertResult); - added++; - } - } - - return { added, updated }; - }); - - try { - const result = transactionResult(); - console.log(`Group sync completed: ${result.added} added, ${result.updated} updated`); - return result; - } catch (error) { - console.error('Error in upsertGroups:', error); - throw error; - } - } + console.log('Marked groups inactive:', inactiveResult); + + for (const group of groups) { + const existing = db.prepare('SELECT 1 FROM groups WHERE id = ?').get(group.id); + console.log('Checking group:', group.id, 'exists:', !!existing); + + if (existing) { + const updateResult = db.prepare( + 'UPDATE groups SET name = ?, active = TRUE, last_verified = CURRENT_TIMESTAMP WHERE id = ?' + ).run(group.subject, group.id); + console.log('Updated group:', group.id, 'result:', updateResult); + updated++; + } else { + const insertResult = db.prepare( + 'INSERT INTO groups (id, community_id, name, active) VALUES (?, ?, ?, TRUE)' + ).run(group.id, env.WHATSAPP_COMMUNITY_ID, group.subject); + console.log('Added group:', group.id, 'result:', insertResult); + added++; + } + } + + return { added, updated }; + }); + + try { + const result = transactionResult(); + console.log(`Group sync completed: ${result.added} added, ${result.updated} updated`); + return result; + } catch (error) { + console.error('Error in upsertGroups:', error); + throw error; + } + } }