This commit is contained in:
2025-05-26 07:45:27 -05:00
commit a1a26e636f
7 changed files with 5950 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
# Directorios y archivos generados automáticamente
node_modules/
npm-debug.log
yarn-error.log
# Archivos de construcción
build/
dist/
out/
# Archivos de configuración locales (pueden variar según tus herramientas)
.env
.env.local
.env.*.local
# Dependencias locales (puedes ajustar esta lista según tus necesidades)
# /node_modules/
# Logs y archivos temporales
*.log
*.tmp
*.swp
# Archivos de usuario y configuración específica de IDE
.idea/
.vscode/
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Dependencias globales de Yarn (opcional; úsalo si lo necesitas)
# /.yarn/
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

+305
View File
@@ -0,0 +1,305 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sistema de descarga automatizado</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root {
--primary-color: #1DA1F2;
--background-color: #15202B;
--card-background: #192734;
--text-color: #FFFFFF;
--border-color: #38444D;
--success-color: #17BF63;
--error-color: #E0245E;
}
body {
font-family: 'Inter', sans-serif;
background-color: var(--background-color);
color: var(--text-color);
margin: 0;
padding: 20px;
min-height: 100vh;
}
.container {
max-width: 800px;
margin: 0 auto;
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 15px 0;
border-bottom: 1px solid var(--border-color);
margin-bottom: 20px;
}
h1 {
font-size: 1.5em;
font-weight: 600;
margin: 0;
}
.url-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.url-container {
background: var(--card-background);
border-radius: 15px;
padding: 20px;
position: relative;
border: 1px solid var(--border-color);
transition: all 0.3s ease;
}
.url-container:hover {
border-color: var(--primary-color);
}
.url-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.url-title {
font-weight: 600;
color: var(--primary-color);
}
.remove-url {
background: transparent;
color: var(--error-color);
border: none;
font-size: 1.2em;
cursor: pointer;
padding: 5px 10px;
border-radius: 50%;
transition: all 0.3s ease;
}
.remove-url:hover {
background: rgba(224, 36, 94, 0.1);
}
.url-inputs {
display: flex;
flex-direction: column;
gap: 15px;
}
input {
background: var(--background-color);
border: 1px solid var(--border-color);
color: var(--text-color);
padding: 12px;
border-radius: 25px;
font-size: 0.9em;
width: 100%;
box-sizing: border-box;
}
input:focus {
border-color: var(--primary-color);
outline: none;
}
.log-container {
margin-top: 15px;
background: var(--background-color);
border-radius: 12px;
padding: 10px;
max-height: 200px;
overflow-y: auto;
font-size: 0.9em;
border: 1px solid var(--border-color);
}
.log-message {
padding: 5px 10px;
border-bottom: 1px solid var(--border-color);
}
.log-message:last-child {
border-bottom: none;
}
.actions {
display: flex;
gap: 10px;
margin-top: 20px;
}
button {
background: var(--primary-color);
color: white;
border: none;
padding: 12px 24px;
border-radius: 25px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
font-size: 0.9em;
}
button:hover {
opacity: 0.9;
}
.add-url {
background: var(--success-color);
}
.status-indicator {
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: 10px;
display: inline-block;
}
.status-pending {
background-color: #FFD700;
}
.status-success {
background-color: var(--success-color);
}
.status-error {
background-color: var(--error-color);
}
/* Scrollbar personalizado */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--background-color);
}
::-webkit-scrollbar-thumb {
background: var(--border-color);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--primary-color);
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Sistema de descarga automatizado</h1>
</div>
<div class="url-list" id="urlList">
<div class="url-container">
<div class="url-header">
<span class="url-title">Proceso #1</span>
<button class="remove-url" onclick="removeUrl(this)">×</button>
</div>
<div class="url-inputs">
<input type="text" class="downloadUrl" placeholder="URL de Descarga" required>
<input type="text" class="ftpUrl" placeholder="Ruta FTP" required>
</div>
<div class="log-container"></div>
</div>
</div>
<div class="actions">
<button class="add-url" onclick="addNewUrl()">Nueva URL</button>
<button id="url_send">Procesar URLs</button>
</div>
</div>
<script>
const { ipcRenderer } = require('electron');
let processCount = 1;
function addNewUrl() {
processCount++;
const urlList = document.getElementById('urlList');
const newUrl = document.createElement('div');
newUrl.className = 'url-container';
newUrl.innerHTML = `
<div class="url-header">
<span class="url-title">Proceso #${processCount}</span>
<button class="remove-url" onclick="removeUrl(this)">×</button>
</div>
<div class="url-inputs">
<input type="text" class="downloadUrl" placeholder="URL de Descarga" required>
<input type="text" class="ftpUrl" placeholder="Ruta FTP" required>
</div>
<div class="log-container"></div>
`;
urlList.appendChild(newUrl);
}
function removeUrl(button) {
const container = button.closest('.url-container');
if (document.getElementsByClassName('url-container').length > 1) {
container.remove();
}
}
document.getElementById('url_send').addEventListener('click', () => {
const urls = [];
const containers = document.getElementsByClassName('url-container');
Array.from(containers).forEach((container, index) => {
const downloadUrl = container.querySelector('.downloadUrl').value;
const ftpUrl = container.querySelector('.ftpUrl').value;
if (downloadUrl && ftpUrl) {
urls.push({
downloadUrl,
ftpUrl,
containerId: index
});
// Limpiar logs anteriores
container.querySelector('.log-container').innerHTML = '';
}
});
if (urls.length > 0) {
ipcRenderer.send('url_process', urls);
}
});
ipcRenderer.on('log_message', (event, message) => {
const containers = document.getElementsByClassName('url-container');
const urlPattern = /URL: (.*?)(?=\s|$)/;
const match = message.match(urlPattern);
if (match) {
const url = match[1];
// Encontrar el contenedor correspondiente
Array.from(containers).forEach(container => {
if (container.querySelector('.downloadUrl').value === url) {
const logContainer = container.querySelector('.log-container');
const logElement = document.createElement('div');
logElement.className = 'log-message';
// Eliminar la URL del mensaje para los logs
const cleanMessage = message.replace(urlPattern, '').trim();
logElement.textContent = cleanMessage;
logContainer.appendChild(logElement);
logContainer.scrollTop = logContainer.scrollHeight;
}
});
}
});
</script>
</body>
</html>
+375
View File
@@ -0,0 +1,375 @@
const { BrowserWindow, app, ipcMain, dialog } = require("electron");
const pie = require("puppeteer-in-electron");
const puppeteer = require("puppeteer");
const AdmZip = require('adm-zip');
const fs = require('fs');
const path = require('path');
const { v4: uuidv4 } = require('uuid');
const os = require('os');
const Client = require("ssh2-sftp-client");
const sftp = new Client();
// Add this function to handle credentials
function handleCredentials() {
const credentialsPath = 'C:/ddbb/credenciales.json';
try {
if (!fs.existsSync('C:/ddbb')) {
fs.mkdirSync('C:/ddbb', { recursive: true });
}
if (fs.existsSync(credentialsPath)) {
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
const credentials = JSON.parse(fileContent);
return credentials;
} else {
const defaultCredentials = {
sftp: {
host: "10.130.20.3",
port: 22,
username: "tarsclsharep",
password: "bD7*oWPahJ4)"
},
sharepoint: {
url: 'https://claromovilco.sharepoint.com/_layouts/15/sharepoint.aspx',
email: '[email protected]',
password: 'QwSqTlSK8%'
}
};
fs.writeFileSync(credentialsPath, JSON.stringify(defaultCredentials, null, 2));
return defaultCredentials;
}
} catch (error) {
console.error('Error handling credentials:', error);
return null;
}
}
// Get credentials and configure CONFIG
const credentials = handleCredentials();
// Configuración
const CONFIG = {
sftp: credentials?.sftp || {
host: "10.130.20.3",
port: 22,
username: "tarsclsharep",
password: "bD7*oWPahJ4)"
},
sharepoint: credentials?.sharepoint || {
url: 'https://claromovilco.sharepoint.com',
email: '[email protected]',
password: 'TittqbAP5rlnR8PQnYLe'
},
downloads: {
folder: path.join(os.homedir(), 'Downloads'),
filePrefix: 'OneDrive'
}
};
let window;
// Funciones auxiliares
async function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Función auxiliar para enviar logs
function sendLog(message) {
if (window) {
window.webContents.send('log_message', message);
}
console.log(message);
}
async function handleSftpConnection(folderToUpload, ftpUrl, downloadUrl) {
sendLog(`URL: ${downloadUrl} >>> Carpeta a subir: ${folderToUpload}`);
sendLog(`URL: ${downloadUrl} >>> Iniciando conexión SFTP...`);
const sftp = new Client();
try {
await sftp.connect(CONFIG.sftp);
sendLog(`URL: ${downloadUrl} >>> Conectado exitosamente al servidor SFTP`);
sendLog(`URL: ${downloadUrl} >>> Iniciando subida de archivos...`);
const filesToUpload = fs.readdirSync(folderToUpload);
// Get current date in format DDMMYY
const today = new Date();
// Add timezone offset to ensure we get the correct local date
today.setMinutes(today.getMinutes() + today.getTimezoneOffset());
const datePrefix = today.getDate().toString().padStart(2, '0') +
(today.getMonth() + 1).toString().padStart(2, '0') +
today.getFullYear().toString().slice(-2);
// Log the date being used for debugging
sendLog(`URL: ${downloadUrl} >>> Buscando archivos con fecha: ${datePrefix}`);
// Filter files that start with today's date
const todayFiles = filesToUpload.filter(file => file.startsWith(datePrefix));
if (todayFiles.length === 0) {
sendLog(`URL: ${downloadUrl} >>> No se encontraron archivos con la fecha actual (${datePrefix})`);
return;
}
for (let i = 0; i < todayFiles.length; i++) {
const localFile = path.join(folderToUpload, todayFiles[i]);
await sftp.put(localFile, ftpUrl + todayFiles[i]);
sendLog(`URL: ${downloadUrl} >>> Archivo ${i + 1} de ${todayFiles.length} subido exitosamente`);
}
sendLog(`URL: ${downloadUrl} >>> Todos los archivos del día ${datePrefix} subidos exitosamente`);
} catch (error) {
sendLog(`URL: ${downloadUrl} >>> Error en la conexión SFTP: ${error}`);
} finally {
await sftp.end();
sendLog(`URL: ${downloadUrl} >>> Conexión SFTP cerrada`);
}
}
async function loginToSharepoint(page) {
sendLog('>>> Iniciando proceso de login en SharePoint...');
await page.goto(CONFIG.sharepoint.url);
sendLog('>>> Ingresando email...');
await page.waitForSelector('input[type="email"]');
await page.type('input[type="email"]', CONFIG.sharepoint.email);
await page.click('input[type="submit"]');
sendLog('>>> Ingresando contraseña...');
await page.waitForSelector('input[type="password"]');
await page.type('input[type="password"]', CONFIG.sharepoint.password);
await delay(1000);
await page.click('input[type="submit"]');
await delay(1000);
// await page.locator('#idBtn_Back').click();
sendLog('>>> Login completado exitosamente');
}
async function handleFileDownload(downloadFolder, downloadUrl) {
sendLog(`URL: ${downloadUrl} >>> Iniciando búsqueda de archivo descargado en: ${downloadFolder}`);
let newFile = null;
let extractFolder = null;
const now = new Date();
for (let i = 0; i < 120; i++) {
sendLog(`URL: ${downloadUrl} >>> Intento ${i + 1} de 120...`);
const files = fs.readdirSync(downloadFolder);
sendLog(`URL: ${downloadUrl} >>> Archivos encontrados en carpeta: ${files.length}`);
const recentFiles = files.filter(file => {
const filePath = path.join(downloadFolder, file);
const fileStat = fs.statSync(filePath);
const isValid = fileStat.isFile() &&
file.startsWith(CONFIG.downloads.filePrefix) &&
fileStat.mtime > now &&
file.endsWith('.zip');
if (isValid) {
sendLog(`URL: ${downloadUrl} >>> Archivo válido encontrado: ${file} (Modificado: ${fileStat.mtime.toISOString()})`);
}
return isValid;
});
if (recentFiles.length > 0) {
newFile = recentFiles[0];
const filePath = path.join(downloadFolder, newFile);
extractFolder = path.join(downloadFolder, 'extracted_' + newFile.replace('.zip', ''));
sendLog(`URL: ${downloadUrl} >>> Archivo encontrado: ${newFile}`);
sendLog(`URL: ${downloadUrl} >>> Ruta completa: ${filePath}`);
sendLog(`URL: ${downloadUrl} >>> Iniciando descompresión en: ${extractFolder}`);
const zip = new AdmZip(filePath);
zip.extractAllTo(extractFolder, true);
// Contar archivos extraídos
const extractedFiles = fs.readdirSync(extractFolder);
sendLog(`URL: ${downloadUrl} >>> Descompresión completada. ${extractedFiles.length} archivos extraídos:`);
extractedFiles.forEach(file => {
sendLog(`URL: ${downloadUrl} >>> - ${file}`);
});
break;
}
await delay(1000);
}
if (!newFile) {
sendLog(`URL: ${downloadUrl} >>> No se encontró ningún archivo nuevo después de 120 segundos.`);
return null;
}
return extractFolder;
}
async function processUrl(downloadUrl, ftpUrl) {
const sendLogWithUrl = (message) => {
sendLog(`URL: ${downloadUrl} ${message}`);
};
let browser = null;
let tempDownloadPath = null;
try {
// Crear carpeta temporal única para este proceso
const processId = uuidv4();
tempDownloadPath = path.join(CONFIG.downloads.folder, `temp_${processId}`);
fs.mkdirSync(tempDownloadPath, { recursive: true });
sendLogWithUrl('>>> Iniciando proceso...');
sendLogWithUrl(`>>> Carpeta temporal creada: ${tempDownloadPath}`);
sendLogWithUrl(`>>> Contenido inicial de la carpeta: ${fs.readdirSync(tempDownloadPath).length} archivos`);
browser = await puppeteer.launch({
headless: false,
executablePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
defaultViewport: null,
args: ['--start-maximized']
});
const page = await browser.newPage();
// Configurar descarga para esta página específica
const client = await page.createCDPSession();
await client.send('Page.setDownloadBehavior', {
behavior: 'allow',
downloadPath: tempDownloadPath
});
await loginToSharepoint(page);
await delay(120000);
await delay(4000);
sendLogWithUrl(`>>> Navegando a: ${downloadUrl}`);
await page.goto(downloadUrl);
await delay(12000);
sendLogWithUrl('>>> Interactuando con la interfaz...');
await page.waitForSelector('.text_a4f5cb66');
await page.locator('::-p-text(Todos los documentos)').click();
await page.locator('::-p-text(Mosaicos)').click();
await delay(1000);
await page.locator('::-p-text(Todos los documentos)').click();
await delay(2000);
sendLogWithUrl('>>> Seleccionando elementos...');
await page.locator('.ms-Check').click();
await delay(500);
await page.keyboard.down('Control');
await page.keyboard.press('a');
await page.keyboard.up('Control');
await delay(2000);
sendLogWithUrl('>>> Iniciando descarga...');
await page.locator('[title="Más comandos"]').click();
await delay(1000);
await page.locator('::-p-text(Descargar)').click();
// Verificar contenido de la carpeta antes de buscar la descarga
sendLogWithUrl(`>>> Contenido de la carpeta antes de la descarga: ${fs.readdirSync(tempDownloadPath).length} archivos`);
await delay(1000);
const extractFolder = await handleFileDownload(tempDownloadPath, downloadUrl);
if (!extractFolder) {
throw new Error('No se pudo encontrar o procesar el archivo descargado');
}
await delay(1000);
await handleSftpConnection(extractFolder, ftpUrl, downloadUrl);
if (browser) {
await browser.close();
}
// Limpiar archivos temporales
try {
fs.rmSync(tempDownloadPath, { recursive: true, force: true });
sendLogWithUrl('>>> Archivos temporales eliminados');
} catch (cleanupError) {
sendLogWithUrl(`>>> Advertencia: No se pudieron eliminar los archivos temporales: ${cleanupError}`);
}
dialog.showMessageBox({
type: 'info',
title: 'Proceso completado',
message: `Proceso completado exitosamente para ${downloadUrl}`
});
sendLogWithUrl(`>>> Proceso completado para: ${downloadUrl}`);
} catch (error) {
sendLogWithUrl(`>>> Error en el proceso: ${error}`);
if (browser) {
await browser.close();
}
// Intentar limpiar archivos temporales en caso de error
if (tempDownloadPath && fs.existsSync(tempDownloadPath)) {
try {
fs.rmSync(tempDownloadPath, { recursive: true, force: true });
sendLogWithUrl('>>> Archivos temporales eliminados después del error');
} catch (cleanupError) {
sendLogWithUrl(`>>> Advertencia: No se pudieron eliminar los archivos temporales: ${cleanupError}`);
}
}
}
}
ipcMain.on('url_process', async (event, urls) => {
try {
// Procesar todas las URLs en paralelo
const promises = urls.map(url => processUrl(url.downloadUrl, url.ftpUrl));
await Promise.all(promises);
sendLog('>>> Todos los procesos han sido completados');
} catch (error) {
sendLog('>>> Error general en el proceso: ' + error);
}
});
// Modificar la inicialización
const main = async () => {
await pie.initialize(app);
};
// Esperar a que la app esté lista
app.whenReady().then(async () => {
window = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
contextIsolation: false,
nodeIntegration: true
}
});
await window.maximize();
await window.removeMenu();
await window.loadFile('index.html');
});
// Manejar el cierre de la aplicación
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', async () => {
if (BrowserWindow.getAllWindows().length === 0) {
window = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
contextIsolation: false,
nodeIntegration: true
}
});
await window.maximize();
await window.removeMenu();
await window.loadFile('index.html');
}
});
main();
+5177
View File
File diff suppressed because it is too large Load Diff
+31
View File
@@ -0,0 +1,31 @@
{
"name": "descargas-bbdd",
"version": "0.0.6",
"main": "main.js",
"scripts": {
"start": "electron .",
"build": "electron-builder"
},
"dependencies": {
"adm-zip": "^0.5.16",
"basic-ftp": "^5.0.5",
"extract-zip": "^2.0.1",
"puppeteer": "^22.15.0",
"puppeteer-core": "^21.11.0",
"puppeteer-in-electron": "^3.0.5",
"ssh2-sftp-client": "^11.0.0",
"uuid": "^11.0.5"
},
"devDependencies": {
"electron": "^20.0.0",
"electron-builder": "^24.9.1"
},
"build": {
"appId": "com.downloads.id",
"productName": "Descargas BBDD",
"win": {
"target": "nsis",
"icon": "icono.ico"
}
}
}
+28
View File
@@ -0,0 +1,28 @@
const puppeteer = require("puppeteer");
// Or import puppeteer from 'puppeteer-core';
const main = async () => {
// Launch the browser and open a new blank page
const browser = await puppeteer.launch({
headless: false
});
const page = await browser.newPage();
// Navigate the page to a URL.
await page.goto('https://claromovilco.sharepoint.com/sites/01Facturacion2/Documentos%20compartidos/Forms/AllItems.aspx?ga=1&id=%2Fsites%2F01Facturacion2%2FDocumentos%20compartidos%2FGestión%20Campañas%20Outbound%2F02%2E%20BRM%2F01%2E%20Campaña%20Tercer%20Anillo%20BRM%2FCampaña%20Tercer%20Anillo%20BRM%20Archivo');
await page.waitForSelector('input[type="email"]');
await page.type('input[type="email"]', '[email protected]');
await page.click('input[type="submit"]');
await page.waitForSelector('input[type="password"]');
await page.type('input[type="password"]', 'TittqbAP5rlnR8PQnYLe');
await new Promise(resolve => setTimeout(resolve, 1000));
await page.click('input[type="submit"]');
await new Promise(resolve => setTimeout(resolve, 1000));
await page.locator('#idBtn_Back').click();
await new Promise(resolve => setTimeout(resolve, 4000));
console.log('ya');
}
main();