/** * PostgreSQL-based auth state for Baileys * Stores credentials and keys in the database instead of files */ import type { AuthenticationCreds, SignalDataTypeMap } from '@whiskeysockets/baileys' import * as baileys from '@whiskeysockets/baileys' import { query } from '../../utils/database' // Get functions from baileys module const initAuthCreds = (baileys as any).initAuthCreds || (baileys as any).default?.initAuthCreds const BufferJSON = (baileys as any).BufferJSON || (baileys as any).default?.BufferJSON const proto = (baileys as any).proto || (baileys as any).default?.proto // Debug: Log what we got console.log('[AuthState] initAuthCreds:', typeof initAuthCreds) console.log('[AuthState] BufferJSON:', typeof BufferJSON) console.log('[AuthState] proto:', typeof proto) export interface PostgresAuthState { state: { creds: AuthenticationCreds keys: { get: (type: T, ids: string[]) => Promise<{ [id: string]: SignalDataTypeMap[T] }> set: (data: { [type: string]: { [id: string]: SignalDataTypeMap[keyof SignalDataTypeMap] | null } }) => Promise } } saveCreds: () => Promise } export async function usePostgresAuthState(instanceId: string): Promise { // Load or create credentials const loadCreds = async (): Promise => { const result = await query<{ key_data: any }>( 'SELECT key_data FROM auth_keys WHERE instance_id = $1 AND key_type = $2 AND key_id = $3', [instanceId, 'creds', 'default'] ) if (result.rows.length > 0 && result.rows[0].key_data) { return JSON.parse(JSON.stringify(result.rows[0].key_data), BufferJSON.reviver) } return initAuthCreds() } // Save credentials const saveCreds = async (creds: AuthenticationCreds): Promise => { const data = JSON.parse(JSON.stringify(creds, BufferJSON.replacer)) await query( `INSERT INTO auth_keys (instance_id, key_type, key_id, key_data) VALUES ($1, $2, $3, $4) ON CONFLICT (instance_id, key_type, key_id) DO UPDATE SET key_data = $4, updated_at = NOW()`, [instanceId, 'creds', 'default', data] ) } // Load keys by type and ids const loadKeys = async ( type: T, ids: string[] ): Promise<{ [id: string]: SignalDataTypeMap[T] }> => { const result: { [id: string]: SignalDataTypeMap[T] } = {} if (ids.length === 0) return result const placeholders = ids.map((_, i) => `$${i + 3}`).join(', ') const queryResult = await query<{ key_id: string; key_data: any }>( `SELECT key_id, key_data FROM auth_keys WHERE instance_id = $1 AND key_type = $2 AND key_id IN (${placeholders})`, [instanceId, type, ...ids] ) for (const row of queryResult.rows) { let value = JSON.parse(JSON.stringify(row.key_data), BufferJSON.reviver) // Handle special types if (type === 'app-state-sync-key' && value) { value = proto.Message.AppStateSyncKeyData.fromObject(value) } result[row.key_id] = value } return result } // Save keys const saveKeys = async ( data: { [type: string]: { [id: string]: SignalDataTypeMap[keyof SignalDataTypeMap] | null } } ): Promise => { for (const type in data) { for (const id in data[type]) { const value = data[type][id] if (value === null) { // Delete key await query( 'DELETE FROM auth_keys WHERE instance_id = $1 AND key_type = $2 AND key_id = $3', [instanceId, type, id] ) } else { // Upsert key const serialized = JSON.parse(JSON.stringify(value, BufferJSON.replacer)) await query( `INSERT INTO auth_keys (instance_id, key_type, key_id, key_data) VALUES ($1, $2, $3, $4) ON CONFLICT (instance_id, key_type, key_id) DO UPDATE SET key_data = $4, updated_at = NOW()`, [instanceId, type, id, serialized] ) } } } } // Load initial credentials const creds = await loadCreds() return { state: { creds, keys: { get: loadKeys, set: saveKeys } }, saveCreds: async () => { await saveCreds(creds) } } } /** * Clear all auth data for an instance */ export async function clearAuthState(instanceId: string): Promise { await query( 'DELETE FROM auth_keys WHERE instance_id = $1', [instanceId] ) }