- Implemented a bash script to test n8n API and retrieve credential schemas. - Added types for API responses, Google Calendar, and WhatsApp instances. - Configured Vitest for testing with React and added setup for testing-library.
194 lines
5.3 KiB
TypeScript
194 lines
5.3 KiB
TypeScript
/**
|
|
* EvolutionAPI Client
|
|
* Integração com EvolutionAPI v2 para gerenciamento de instâncias WhatsApp
|
|
* Documentação: https://doc.evolution-api.com/v2/
|
|
*/
|
|
|
|
import type {
|
|
ConnectionStateAPIResponse,
|
|
InstanceStatus,
|
|
LogoutResponse,
|
|
QRCodeResponse,
|
|
} from "@/types/whatsapp";
|
|
|
|
// Re-export InstanceStatus for convenience
|
|
export type { InstanceStatus } from "@/types/whatsapp";
|
|
|
|
const EVOLUTION_API_URL = process.env.EVOLUTION_API_URL ?? "";
|
|
const EVOLUTION_API_KEY = process.env.EVOLUTION_API_KEY ?? "";
|
|
|
|
/**
|
|
* Get list of instance names from env
|
|
* Parse EVOLUTION_INSTANCE_NAMES (comma-separated) into array
|
|
*/
|
|
export function getInstanceNames(): string[] {
|
|
const names = process.env.EVOLUTION_INSTANCE_NAMES || "";
|
|
return names
|
|
.split(",")
|
|
.map((n) => n.trim())
|
|
.filter(Boolean);
|
|
}
|
|
|
|
/**
|
|
* Get status of a specific instance
|
|
* Endpoint: GET /instance/connectionState/{instance}
|
|
* Doc: https://doc.evolution-api.com/v2/api-reference/instance-controller/connection-state
|
|
*/
|
|
export async function getInstanceStatus(
|
|
instanceName: string,
|
|
): Promise<InstanceStatus> {
|
|
try {
|
|
console.log(`[EvolutionAPI] Fetching status for instance: ${instanceName}`);
|
|
|
|
const response = await fetch(
|
|
`${EVOLUTION_API_URL}/instance/connectionState/${encodeURIComponent(instanceName)}`,
|
|
{
|
|
method: "GET",
|
|
headers: {
|
|
apikey: EVOLUTION_API_KEY,
|
|
"Content-Type": "application/json",
|
|
},
|
|
},
|
|
);
|
|
|
|
if (!response.ok) {
|
|
if (response.status === 404) {
|
|
throw new Error(`Instance "${instanceName}" does not exist`);
|
|
}
|
|
throw new Error(`API returned ${response.status}`);
|
|
}
|
|
|
|
const data: ConnectionStateAPIResponse = await response.json();
|
|
console.log(`[EvolutionAPI] Status response:`, data);
|
|
|
|
// Mapear estado da API para nosso formato
|
|
// Estados possíveis: "open", "close", etc
|
|
const status =
|
|
data.instance.state === "open" ? "connected" : "disconnected";
|
|
|
|
return {
|
|
instance: instanceName,
|
|
status,
|
|
};
|
|
} catch (error) {
|
|
const errorMessage =
|
|
error instanceof Error ? error.message : "Unknown error";
|
|
console.error(
|
|
`[EvolutionAPI] Error fetching status for ${instanceName}:`,
|
|
error,
|
|
);
|
|
return {
|
|
instance: instanceName,
|
|
status: "error",
|
|
error: errorMessage,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get status for all instances
|
|
*/
|
|
export async function getAllInstancesStatus(): Promise<InstanceStatus[]> {
|
|
const instances = getInstanceNames();
|
|
const promises = instances.map((name) => getInstanceStatus(name));
|
|
return Promise.all(promises);
|
|
}
|
|
|
|
/**
|
|
* Generate pairing code for instance connection
|
|
* Endpoint: GET /instance/connect/{instance}
|
|
* Doc: https://doc.evolution-api.com/v2/api-reference/instance-controller/instance-connect
|
|
*
|
|
* @param instanceName Nome da instância
|
|
* @param phoneNumber Número de telefone com código do país (opcional)
|
|
* @returns QRCodeResponse com pairingCode, code e count
|
|
*/
|
|
export async function generateQRCode(
|
|
instanceName: string,
|
|
phoneNumber?: string,
|
|
): Promise<QRCodeResponse> {
|
|
try {
|
|
console.log(
|
|
`[EvolutionAPI] Generating QR code for instance: ${instanceName}`,
|
|
);
|
|
|
|
const url = new URL(
|
|
`${EVOLUTION_API_URL}/instance/connect/${encodeURIComponent(instanceName)}`,
|
|
);
|
|
if (phoneNumber) {
|
|
url.searchParams.append("number", phoneNumber);
|
|
}
|
|
|
|
const response = await fetch(url.toString(), {
|
|
method: "GET",
|
|
headers: {
|
|
apikey: EVOLUTION_API_KEY,
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
|
|
if (!response.ok) {
|
|
if (response.status === 404) {
|
|
throw new Error(`Instance "${instanceName}" does not exist`);
|
|
}
|
|
throw new Error(`API returned ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
console.log(`[EvolutionAPI] QR code response:`, data);
|
|
|
|
return {
|
|
pairingCode: data.pairingCode,
|
|
code: data.code,
|
|
count: data.count,
|
|
base64: data.base64,
|
|
qrcode: data.qrcode,
|
|
instance: instanceName,
|
|
};
|
|
} catch (error) {
|
|
console.error(
|
|
`[EvolutionAPI] Error generating QR code for ${instanceName}:`,
|
|
error,
|
|
);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Disconnect instance (logout)
|
|
* Endpoint: DELETE /instance/logout/{instance}
|
|
* Doc: https://doc.evolution-api.com/v2/api-reference/instance-controller/logout-instance
|
|
*/
|
|
export async function disconnectInstance(instanceName: string): Promise<void> {
|
|
try {
|
|
console.log(`[EvolutionAPI] Disconnecting instance: ${instanceName}`);
|
|
|
|
const response = await fetch(
|
|
`${EVOLUTION_API_URL}/instance/logout/${encodeURIComponent(instanceName)}`,
|
|
{
|
|
method: "DELETE",
|
|
headers: {
|
|
apikey: EVOLUTION_API_KEY,
|
|
"Content-Type": "application/json",
|
|
},
|
|
},
|
|
);
|
|
|
|
if (!response.ok) {
|
|
if (response.status === 404) {
|
|
throw new Error(`Instance "${instanceName}" does not exist`);
|
|
}
|
|
throw new Error(`API returned ${response.status}`);
|
|
}
|
|
|
|
const data: LogoutResponse = await response.json();
|
|
console.log(
|
|
`[EvolutionAPI] Instance ${instanceName} disconnected:`,
|
|
data.response.message,
|
|
);
|
|
} catch (error) {
|
|
console.error(`[EvolutionAPI] Error disconnecting ${instanceName}:`, error);
|
|
throw error;
|
|
}
|
|
}
|