import path, { resolve } from "path"; import { app, ipcMain } from "electron"; import serve from "electron-serve"; import { createWindow } from "./helpers"; import { SerialPort } from "serialport"; import { print } from "pdf-to-printer"; import fs from "fs"; import download from "download"; const isProd = process.env.NODE_ENV === "production"; if (isProd) { serve({ directory: "app" }); } else { app.setPath("userData", `${app.getPath("userData")} (development)`); } (async () => { await app.whenReady(); const mainWindow = createWindow("main", { width: 1000, height: 600, webPreferences: { preload: path.join(__dirname, "preload.js"), }, }); if (isProd) { await mainWindow.loadURL("app://./home"); mainWindow.setMenuBarVisibility(false); mainWindow.setFullScreen(true); } else { const port = process.argv[2]; await mainWindow.loadURL(`http://localhost:${port}/configuracionMonedero`); mainWindow.setMenuBarVisibility(false); mainWindow.webContents.openDevTools(); } })(); app.on("window-all-closed", () => { app.quit(); }); ipcMain.on("message", async (event, arg) => { event.reply("message", `${arg} World!`); }); /* Variables */ const api = process.env.REACT_APP_ROOT; let PuertoSerialMonedero = null; let dinero: number = 0; /* Configuracion del kiosco*/ ipcMain.on("getApi", async (event, arg) => { event.reply("getApi", api); }); ipcMain.on("BuscarMonedero", async (event, path) => { try { const list = await SerialPort.list(); if (list.length === 0) { throw new Error("La lista de puertos está vacia"); } const puerto = buscarPuerto(list); if (!puerto) { throw new Error("No se pudo detectar el monedero"); } const puertoMonedero = puerto.path; event.sender.send("GetPuertoMonedero", `${puertoMonedero}`); } catch (error) { console.log("Ha ocurrido un error en: Monedero"); } }); ipcMain.on("AbrirPuerto", async (event, path) => { try { if (!PuertoSerialMonedero) { // Si el puerto es null, creamos una nueva instancia del puerto serie PuertoSerialMonedero = new SerialPort({ path: path, baudRate: 9600, }); // Evento 'open' para cuando el puerto se abre con éxito PuertoSerialMonedero.on("open", () => { event.reply("EstadoPuerto", { abierto: true }); // Enviar mensaje si el puerto está abierto }); // Evento 'error' para manejar errores que puedan ocurrir durante la operación del puerto PuertoSerialMonedero.on("error", (error) => { console.error("Error en Puerto Serial:", error); event.reply("EstadoPuerto", { abierto: false }); // Enviar mensaje si se produce un error al abrir el puerto }); } else if (!PuertoSerialMonedero.isOpen) { // Si el puerto existe pero está cerrado, intentamos abrirlo PuertoSerialMonedero.open((error) => { if (error) { console.error("Error al abrir el puerto:", error); event.reply("EstadoPuerto", { abierto: false }); // Enviar mensaje si se produce un error al abrir el puerto } else { event.reply("EstadoPuerto", { abierto: true }); // Enviar mensaje si el puerto está abierto } }); } else { // Si el puerto ya está abierto, mostramos un mensaje indicando que ya está en uso console.log("El Puerto Serial ya está abierto"); event.reply("EstadoPuerto", { abierto: true }); // Enviar mensaje si el puerto está abierto } PuertoSerialMonedero.on("data", (data) => { let coin = parseInt(data, 10); dinero = dinero + coin; console.log("Dinero Depositado: ", dinero); event.sender.send("CantidadDinero", dinero); }); } catch (error) { console.error("Ha ocurrido un error en: AbrirPuerto", error); event.sender.send("EstadoPuerto", { abierto: false }); // Enviar mensaje si se produce un error } }); ipcMain.on("openDial", async (event, data) => { console.log("Peticion de impresion"); try { if (!data.api || !data.fileId || !data.token) { throw new Error("Datos incompletos"); } const filePath = await descargarArchivo(data.api, data.fileId, data.token); const res = await imprimirArchivo(filePath); /* console.log(res); */ if (res) { console.log("Proceso Main de impresion finalizado con exito"); event.reply("finishPrint", data.fileId); } // Limpiar eventos adicionales si es necesario event.reply("cleanFinishPrint", ""); } catch (error) { console.error(error); // Enviar mensaje de error al cliente event.reply("finishPrintError", error.message); } }); /* Auxiliares */ const buscarPuerto = (puertos) => { for (const puerto of puertos) { const friendlyName = puerto.friendlyName.substring(0, 10); if (friendlyName === "USB-SERIAL") { return puerto; } } return undefined; }; ipcMain.on("getApi", async (event, arg) => { event.reply("getApi", api); }); ipcMain.on("LimpiarDinero", (event, path) => { dinero = 0; console.log("Dinero Limpio: ", dinero); event.reply("LimpiarDinero", dinero); }); ipcMain.on("statusPuerto", async (event, arg) => { try { if (PuertoSerialMonedero && PuertoSerialMonedero.isOpen) { event.reply("statusPuerto", { abierto: true }); } else { event.reply("statusPuerto", { abierto: false }); } } catch (error) { console.error("Error al verificar estado del puerto:", error); event.reply("statusPuerto", { abierto: false }); // Enviar mensaje de estado false en caso de error } }); async function descargarArchivo(api, fileId, token) { const url = `${api}/getFile/${fileId}/${token}`; const filePath = "public/file.pdf"; await download(url).pipe(fs.createWriteStream(filePath)); return filePath; } async function imprimirArchivo(filePath) { try { await print(filePath, {}); console.log("Exito al imprimir"); return true; } catch (error) { console.log("Error al imprimir", error); throw error; } } import { BrowserWindow, BrowserView, WebContentsView } from "electron"; import { URL } from "url"; import axios from "axios"; ipcMain.handle("login-with-google", async () => { return new Promise(async (resolve, reject) => { const authWindow = new BrowserWindow({ width: 800, height: 700, fullscreen: true, frame: false, webPreferences: { nodeIntegration: false, contextIsolation: true, }, }); await authWindow.webContents.session.clearStorageData({ storages: ["cookies"], }); await authWindow.loadURL("http://localhost:3000/persona/google"); // --------------------------------------------------------- // 🔹 Teclado virtual en WebContentsView // --------------------------------------------------------- const keyboardView = new BrowserView({ webPreferences: { preload: path.join(__dirname, "preload.js"), nodeIntegration: false, contextIsolation: true, }, }); authWindow.addBrowserView(keyboardView); keyboardView.webContents.loadURL("http://localhost:8888/teclado"); const bounds = authWindow.getBounds(); keyboardView.setBounds({ x: 0, y: bounds.height - 260, // altura del teclado width: bounds.width, height: 260, }); keyboardView.setAutoResize({ width: true, horizontal: true, vertical: false, }); // --------------------------------------------------------- // BOTÓN DE CANCELAR const cancelView = new BrowserView({ webPreferences: { nodeIntegration: true, contextIsolation: false, }, }); authWindow.addBrowserView(cancelView); cancelView.setBounds({ x: 1000, y: 50, width: 50, height: 50 }); cancelView.webContents.loadURL( "data:text/html," + encodeURIComponent(` `) ); ipcMain.once("google-login-cancel", () => { authWindow.close(); reject(new Error("Login cancelado por el usuario")); }); authWindow.webContents.on("will-redirect", async (event, url) => { if (url.startsWith("http://localhost:8888/oauth-callback")) { event.preventDefault(); const token = new URL(url).searchParams.get("token"); const error = new URL(url).searchParams.get("error"); if (error || !token) { authWindow.close(); return reject(new Error("Login cancelado o fallido")); } try { const response = await axios.get("http://localhost:3000/persona/me", { headers: { Authorization: `Bearer ${token}` }, }); resolve({ data: { ...response.data.data, token } }); } catch (err) { reject(err); } finally { authWindow.close(); } } }); authWindow.on("closed", () => reject(new Error("Login cancelado"))); }); });