From 1661f392fcff5d910cf6c548078a6911bb633c2d Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Wed, 15 Oct 2025 10:40:45 +0000
Subject: [PATCH] =?UTF-8?q?feat:=20Implementa=20la=20funcionalidad=20de=20?=
=?UTF-8?q?m=C3=BAltiples=20servidores?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Modifica la configuración para admitir una lista de servidores.
- Añade un Contexto de React para gestionar el servidor seleccionado.
- Crea un componente de interfaz de usuario para que los usuarios puedan cambiar de servidor.
- Actualiza el servicio de la API para utilizar la configuración del servidor seleccionado en todas las llamadas.
---
npm_output.log | 3 +
package-lock.json | 3 +
src/app/components/ServerSelector.js | 31 ++++++++
src/app/config.js | 24 ++++---
src/app/context/ServerContext.js | 24 +++++++
src/app/layout.js | 8 ++-
src/app/page.js | 47 ++++++------
src/app/services/evolution.api.service.js | 88 ++++++++++-------------
yarn.lock | 36 ++++++++--
9 files changed, 176 insertions(+), 88 deletions(-)
create mode 100644 npm_output.log
create mode 100644 src/app/components/ServerSelector.js
create mode 100644 src/app/context/ServerContext.js
diff --git a/npm_output.log b/npm_output.log
new file mode 100644
index 0000000..0655e8a
--- /dev/null
+++ b/npm_output.log
@@ -0,0 +1,3 @@
+
+> evolution_api_qr_code@0.1.0 dev
+> next dev --turbopack -p 5000
diff --git a/package-lock.json b/package-lock.json
index 1a211ae..4a89f50 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -575,6 +575,7 @@
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"license": "MIT",
+ "peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
@@ -1140,6 +1141,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -1149,6 +1151,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"scheduler": "^0.25.0"
},
diff --git a/src/app/components/ServerSelector.js b/src/app/components/ServerSelector.js
new file mode 100644
index 0000000..a2a652d
--- /dev/null
+++ b/src/app/components/ServerSelector.js
@@ -0,0 +1,31 @@
+"use client";
+import React from 'react';
+import { useServer } from '../context/ServerContext';
+
+const ServerSelector = () => {
+ const { selectedServer, changeServer, servers } = useServer();
+
+ const handleServerChange = (e) => {
+ changeServer(e.target.value);
+ };
+
+ return (
+
+
+
+
+ );
+};
+
+export default ServerSelector;
\ No newline at end of file
diff --git a/src/app/config.js b/src/app/config.js
index a7541ba..e658130 100644
--- a/src/app/config.js
+++ b/src/app/config.js
@@ -1,9 +1,17 @@
+const SERVERS = [
+ {
+ name: "Servidor Principal",
+ url: process.env.EVOLUTION_API_URL || "http://localhost",
+ port: process.env.EVOLUTION_API_PORT || 8080,
+ apikey: process.env.EVOLUTION_API_KEY || "429683C4C977415CAAFCCE10F7D57E11"
+ }
+ // Añade más servidores aquí si es necesario
+ // {
+ // name: "Otro Servidor",
+ // url: "http://otro-servidor.com",
+ // port: 8081,
+ // apikey: "OTRA_API_KEY"
+ // }
+];
-const CONFIG = {
- EVOLUTION_API_URL : process.env.EVOLUTION_API_URL || "http://localhost",
- EVOLUTION_API_PORT : process.env.EVOLUTION_API_PORT || 8080,
- EVOLUTION_API_KEY : process.env.EVOLUTION_API_KEY || "429683C4C977415CAAFCCE10F7D57E11"
-}
-
-
-export default CONFIG
\ No newline at end of file
+export default SERVERS;
\ No newline at end of file
diff --git a/src/app/context/ServerContext.js b/src/app/context/ServerContext.js
new file mode 100644
index 0000000..7651246
--- /dev/null
+++ b/src/app/context/ServerContext.js
@@ -0,0 +1,24 @@
+"use client";
+import React, { createContext, useState, useContext } from 'react';
+import SERVERS from '../config';
+
+const ServerContext = createContext();
+
+export const ServerProvider = ({ children }) => {
+ const [selectedServer, setSelectedServer] = useState(SERVERS[0]);
+
+ const changeServer = (serverName) => {
+ const server = SERVERS.find(s => s.name === serverName);
+ if (server) {
+ setSelectedServer(server);
+ }
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useServer = () => useContext(ServerContext);
\ No newline at end of file
diff --git a/src/app/layout.js b/src/app/layout.js
index 3607222..7270971 100644
--- a/src/app/layout.js
+++ b/src/app/layout.js
@@ -1,6 +1,8 @@
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
-import 'bootstrap/dist/css/bootstrap.min.css'
+import 'bootstrap/dist/css/bootstrap.min.css';
+import { ServerProvider } from './context/ServerContext';
+
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
@@ -20,7 +22,9 @@ export default function RootLayout({ children }) {
return (
- {children}
+
+ {children}
+
);
diff --git a/src/app/page.js b/src/app/page.js
index c5fe9db..39ec04d 100644
--- a/src/app/page.js
+++ b/src/app/page.js
@@ -11,9 +11,12 @@ import { connectionState, fetchInstances, getQr } from "./services/evolution.api
import { getCountryCodes } from "./services/country.codes";
import { CIcon } from '@coreui/icons-react';
import { cilReload } from '@coreui/icons';
+import ServerSelector from './components/ServerSelector';
+import { useServer } from './context/ServerContext';
export default function Home() {
const countryList = getCountryCodes();
+ const { selectedServer } = useServer();
const [countryCode, setCountryCode] = useState(countryList[0].code);
const [phoneNumber, setPhoneNumber] = useState("");
const [toasts, setToasts] = useState([]);
@@ -26,27 +29,29 @@ export default function Home() {
useEffect(() => {
const loadInstances = async () => {
- try {
- const { success, response } = await fetchInstances();
- if (success) {
- setInstances(response.data);
- showToast(response.msg, 'success');
- } else {
- showToast("Error al obtener las instancias", 'danger');
+ if (selectedServer) {
+ try {
+ const { success, response } = await fetchInstances(selectedServer);
+ if (success) {
+ setInstances(response.data);
+ showToast(response.msg, 'success');
+ } else {
+ showToast("Error al obtener las instancias", 'danger');
+ }
+ } catch (error) {
+ console.error(error);
+ showToast("Error de conexión", 'danger');
}
- } catch (error) {
- console.error(error);
- showToast("Error de conexión", 'danger');
}
};
loadInstances();
- }, []);
+ }, [selectedServer]);
useEffect(() => {
if (selectedInstance && (selectedInstance.state === 'close' || selectedInstance.state === 'connecting') && qrAttempts < 3) {
const interval = setInterval(async () => {
if (qrAttempts < 3) {
- const { success, response } = await getQr(selectedInstance.name);
+ const { success, response } = await getQr(selectedInstance.name, selectedServer);
if (success) {
setQR(response.data.base64);
setCountdown(45); // Reinicia el contador
@@ -62,7 +67,7 @@ export default function Home() {
return () => clearInterval(interval);
}
- }, [selectedInstance, qrAttempts]);
+ }, [selectedInstance, qrAttempts, selectedServer]);
useEffect(() => {
if (selectedInstance && (selectedInstance.state === 'close' || selectedInstance.state === 'connecting')) {
@@ -70,11 +75,9 @@ export default function Home() {
setQrRefresh(false); // Asegura que el icono esté oculto al inicio
const countdownInterval = setInterval(() => {
- console.log(qrAttempts)
setCountdown(prev => {
- // Muestra el icono solo cuando faltan 3 segundos
if (prev === 1 && qrAttempts === 3) {
- setQrRefresh(true); // Activa el icono cuando faltan 3 segundos
+ setQrRefresh(true);
}
return prev > 0 ? prev - 1 : 0;
});
@@ -84,7 +87,6 @@ export default function Home() {
}
}, [selectedInstance, qrAttempts]);
-
const showToast = (message, color) => {
const id = Date.now();
setToasts((prevToasts) => [...prevToasts, { id, message, color, visible: true }]);
@@ -95,7 +97,7 @@ export default function Home() {
e.preventDefault();
setQrAttempts(0);
setQrRefresh(false);
- setCountdown(45); // Reinicia el contador cuando se envía el formulario
+ setCountdown(45);
const finalNumber = `${countryCode}${phoneNumber}`;
const foundInstance = instances.find(instance => instance.number === finalNumber);
@@ -104,13 +106,13 @@ export default function Home() {
showToast(`Número ${finalNumber} encontrado ✅`, 'success');
try {
- const { success, response } = await connectionState(foundInstance.name);
+ const { success, response } = await connectionState(foundInstance.name, selectedServer);
if (success) {
const instanceState = response.data.instance.state;
setSelectedInstance(prev => ({ ...prev, state: instanceState }));
if (instanceState === 'close' || instanceState === 'connecting') {
- const { success: qrSuccess, response: qrResponse } = await getQr(foundInstance.name);
+ const { success: qrSuccess, response: qrResponse } = await getQr(foundInstance.name, selectedServer);
if (qrSuccess) {
setQR(qrResponse.data.base64);
setCountdown(45);
@@ -135,7 +137,7 @@ export default function Home() {
setQrAttempts(0);
setQrRefresh(false);
if (selectedInstance) {
- const { success, response } = await getQr(selectedInstance.name);
+ const { success, response } = await getQr(selectedInstance.name, selectedServer);
if (success) {
setQR(response.data.base64);
setCountdown(45);
@@ -152,6 +154,7 @@ export default function Home() {
INGRESE SU NUMERO
+
setCountryCode(e.target.value)}>
@@ -223,4 +226,4 @@ export default function Home() {
>
);
-}
+}
\ No newline at end of file
diff --git a/src/app/services/evolution.api.service.js b/src/app/services/evolution.api.service.js
index 3f57052..08c3066 100644
--- a/src/app/services/evolution.api.service.js
+++ b/src/app/services/evolution.api.service.js
@@ -1,73 +1,61 @@
import axios from 'axios';
-import CONFIG from '../config'
-const fetchInstances = async () => {
+
+const fetchInstances = async (server) => {
return new Promise(async (resolve, reject) => {
- try{
- const server = `${CONFIG.EVOLUTION_API_URL}:${CONFIG.EVOLUTION_API_PORT}`
- const uri = `${server}/instance/fetchInstances`
+ try {
+ const { url, port, apikey } = server;
+ const uri = `${url}:${port}/instance/fetchInstances`;
axios.get(uri, {
- headers : {Apikey : CONFIG.EVOLUTION_API_KEY}
+ headers: { 'apikey': apikey }
}).then(response => {
- resolve({success : true, response : {msg : "Instancias obtenidas satisfactoriamente", data : response.data}})
+ resolve({ success: true, response: { msg: "Instancias obtenidas satisfactoriamente", data: response.data } });
}).catch(err => {
- reject({success : false, response : {msg : "Ocurrio un error al consultar el estado de conexion", error : JSON.stringify(err)}})
- })
-
-
- }catch(err){
- reject({success:false, response : {msg : "Ocurrion un error inesperado al obtener el estado de conexion", error : JSON.stringify(err)}})
+ reject({ success: false, response: { msg: "Ocurrió un error al consultar las instancias", error: err.message } });
+ });
+ } catch (err) {
+ reject({ success: false, response: { msg: "Ocurrió un error inesperado al obtener las instancias", error: err.message } });
}
- })
-}
-
+ });
+};
-
-const connectionState = async (instance) => {
+const connectionState = async (instance, server) => {
return new Promise(async (resolve, reject) => {
- try{
- const server = `${CONFIG.EVOLUTION_API_URL}:${CONFIG.EVOLUTION_API_PORT}`
- const uri = `${server}/instance/connectionState/${instance}`
+ try {
+ const { url, port, apikey } = server;
+ const uri = `${url}:${port}/instance/connectionState/${instance}`;
axios.get(uri, {
- headers : {Apikey : CONFIG.EVOLUTION_API_KEY}
+ headers: { 'apikey': apikey }
}).then(response => {
- resolve({success : true, response : {msg : "Estado de conexion obtenido", data : response.data}})
+ resolve({ success: true, response: { msg: "Estado de conexión obtenido", data: response.data } });
}).catch(err => {
- reject({success : false, response : {msg : "Ocurrio un error al consultar el estado de conexion", error : JSON.stringify(err)}})
- })
-
-
- }catch(err){
- reject({success:false, response : {msg : "Ocurrion un error inesperado al obtener el estado de conexion", error : JSON.stringify(err)}})
+ reject({ success: false, response: { msg: "Ocurrió un error al consultar el estado de conexión", error: err.message } });
+ });
+ } catch (err) {
+ reject({ success: false, response: { msg: "Ocurrió un error inesperado al obtener el estado de conexión", error: err.message } });
}
- })
-}
+ });
+};
-
-const getQr = async (name) => {
+const getQr = async (name, server) => {
return new Promise(async (resolve, reject) => {
- try{
- const server = `${CONFIG.EVOLUTION_API_URL}:${CONFIG.EVOLUTION_API_PORT}`
- const uri = `${server}/instance/connect/${name}`
+ try {
+ const { url, port, apikey } = server;
+ const uri = `${url}:${port}/instance/connect/${name}`;
axios.get(uri, {
- headers : {Apikey : CONFIG.EVOLUTION_API_KEY}
+ headers: { 'apikey': apikey }
}).then(response => {
- resolve({success : true, response : {msg : "Codigo QR obtenido satisfactoriamente", data : response.data}})
+ resolve({ success: true, response: { msg: "Código QR obtenido satisfactoriamente", data: response.data } });
}).catch(err => {
- reject({success : false, response : {msg : "Ocurrio un error al obtener el codigo QR", error : JSON.stringify(err)}})
- })
-
-
- }catch(err){
- reject({success:false, response : {msg : "Ocurrion un error inesperado al obtener el codigo QR", error : JSON.stringify(err)}})
+ reject({ success: false, response: { msg: "Ocurrió un error al obtener el código QR", error: err.message } });
+ });
+ } catch (err) {
+ reject({ success: false, response: { msg: "Ocurrió un error inesperado al obtener el código QR", error: err.message } });
}
- })
-}
-
-
-
+ });
+};
export {
fetchInstances,
getQr,
connectionState
-}
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 031a722..e145bdf 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -26,20 +26,44 @@
"@popperjs/core" "^2.11.8"
prop-types "^15.8.1"
-"@img/sharp-win32-x64@0.33.5":
+"@img/sharp-libvips-linux-x64@1.0.4":
+ version "1.0.4"
+ resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz"
+ integrity sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==
+
+"@img/sharp-libvips-linuxmusl-x64@1.0.4":
+ version "1.0.4"
+ resolved "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz"
+ integrity sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==
+
+"@img/sharp-linux-x64@0.33.5":
+ version "0.33.5"
+ resolved "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz"
+ integrity sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-x64" "1.0.4"
+
+"@img/sharp-linuxmusl-x64@0.33.5":
version "0.33.5"
- resolved "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz"
- integrity sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==
+ resolved "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz"
+ integrity sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==
+ optionalDependencies:
+ "@img/sharp-libvips-linuxmusl-x64" "1.0.4"
"@next/env@15.1.7":
version "15.1.7"
resolved "https://registry.npmjs.org/@next/env/-/env-15.1.7.tgz"
integrity sha512-d9jnRrkuOH7Mhi+LHav2XW91HOgTAWHxjMPkXMGBc9B2b7614P7kjt8tAplRvJpbSt4nbO1lugcT/kAaWzjlLQ==
-"@next/swc-win32-x64-msvc@15.1.7":
+"@next/swc-linux-x64-gnu@15.1.7":
+ version "15.1.7"
+ resolved "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.7.tgz"
+ integrity sha512-GOzXutxuLvLHFDAPsMP2zDBMl1vfUHHpdNpFGhxu90jEzH6nNIgmtw/s1MDwpTOiM+MT5V8+I1hmVFeAUhkbgQ==
+
+"@next/swc-linux-x64-musl@15.1.7":
version "15.1.7"
- resolved "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.7.tgz"
- integrity sha512-dC01f1quuf97viOfW05/K8XYv2iuBgAxJZl7mbCKEjMgdQl5JjAKJ0D2qMKZCgPWDeFbFT0Q0nYWwytEW0DWTQ==
+ resolved "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.7.tgz"
+ integrity sha512-WrZ7jBhR7ATW1z5iEQ0ZJfE2twCNSXbpCSaAunF3BKcVeHFADSI/AW1y5Xt3DzTqPF1FzQlwQTewqetAABhZRQ==
"@popperjs/core@^2.11.8":
version "2.11.8"