Rename Wallamonitor to Wallabicher across all files and update related configurations in Docker and documentation. Adjusted service names, container names, and references in scripts and frontend components to reflect the new naming convention.
This commit is contained in:
12
DOCKER.md
12
DOCKER.md
@@ -1,6 +1,6 @@
|
||||
# 🐳 Guía de Docker para Wallamonitor
|
||||
# 🐳 Guía de Docker para Wallabicher
|
||||
|
||||
Esta guía explica cómo ejecutar Wallamonitor usando Docker Compose.
|
||||
Esta guía explica cómo ejecutar Wallabicher usando Docker Compose.
|
||||
|
||||
## 📋 Requisitos Previos
|
||||
|
||||
@@ -32,7 +32,7 @@ Esto iniciará:
|
||||
- **Redis** (puerto 6379) - Cache de artículos
|
||||
- **Backend** (puerto 3001) - API Node.js
|
||||
- **Frontend** (puerto 3000) - Interfaz web Vue
|
||||
- **Wallamonitor Python** - Servicio principal de monitoreo
|
||||
- **Wallabicher Python** - Servicio principal de monitoreo
|
||||
|
||||
### 3. Acceder a la interfaz
|
||||
|
||||
@@ -56,7 +56,7 @@ Abre tu navegador en: **http://localhost:3000**
|
||||
- **URL**: http://localhost:3000
|
||||
- **Funciones**: Interfaz web moderna
|
||||
|
||||
### Wallamonitor (Python)
|
||||
### Wallabicher (Python)
|
||||
- **Sin puertos expuestos** (solo comunicación interna)
|
||||
- **Funciones**: Monitoreo de marketplaces y envío de notificaciones
|
||||
|
||||
@@ -68,7 +68,7 @@ Abre tu navegador en: **http://localhost:3000**
|
||||
docker-compose logs -f
|
||||
|
||||
# Servicio específico
|
||||
docker-compose logs -f wallamonitor
|
||||
docker-compose logs -f wallabicher
|
||||
docker-compose logs -f backend
|
||||
docker-compose logs -f frontend
|
||||
```
|
||||
@@ -92,7 +92,7 @@ docker-compose up -d
|
||||
### Reiniciar un servicio específico
|
||||
```bash
|
||||
docker-compose restart backend
|
||||
docker-compose restart wallamonitor
|
||||
docker-compose restart wallabicher
|
||||
```
|
||||
|
||||
### Ver estado de servicios
|
||||
|
||||
@@ -30,6 +30,6 @@ COPY . .
|
||||
|
||||
# Healthcheck simple (verifica que el proceso esté ejecutándose)
|
||||
HEALTHCHECK --interval=60s --timeout=10s --start-period=30s --retries=3 \
|
||||
CMD pgrep -f "python.*wallamonitor.py" || exit 1
|
||||
CMD pgrep -f "python.*wallabicher.py" || exit 1
|
||||
|
||||
CMD ["python", "wallamonitor.py"]
|
||||
CMD ["python", "wallabicher.py"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Guía Rápida de Inicio - WallaMonitor 🚀
|
||||
# Guía Rápida de Inicio - Wallabicher 🚀
|
||||
|
||||
Esta guía te ayudará a configurar y ejecutar WallaMonitor en menos de 5 minutos.
|
||||
Esta guía te ayudará a configurar y ejecutar Wallabicher en menos de 5 minutos.
|
||||
|
||||
## Requisitos Previos
|
||||
|
||||
@@ -79,12 +79,12 @@ El archivo `workers.json` contiene ejemplos de búsquedas. Personalízalo según
|
||||
- `latitude`/`longitude`: Para búsquedas locales (opcional)
|
||||
- `max_distance`: Distancia máxima en km (opcional)
|
||||
|
||||
## Ejecutar WallaMonitor
|
||||
## Ejecutar Wallabicher
|
||||
|
||||
Una vez configurado, simplemente ejecuta:
|
||||
|
||||
```bash
|
||||
python wallamonitor.py
|
||||
python wallabicher.py
|
||||
```
|
||||
|
||||
El monitor:
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
### Estructura del Proyecto
|
||||
|
||||
```
|
||||
wallamonitor/
|
||||
wallabicher/
|
||||
├── platforms/ # Implementaciones de plataformas
|
||||
│ ├── base_platform.py # Clase abstracta base
|
||||
│ ├── platform_factory.py # Factory para crear plataformas
|
||||
@@ -214,7 +214,7 @@
|
||||
2. Ejecuta Wallabicher:
|
||||
|
||||
```bash
|
||||
python3 wallamonitor.py
|
||||
python3 wallabicher.py
|
||||
```
|
||||
|
||||
El bot revisará Wallapop periódicamente (configurable, por defecto cada 30s) y enviará notificaciones a tu canal o chat de Telegram siempre que aparezcan artículos nuevos que encajen con tus filtros.
|
||||
|
||||
@@ -47,7 +47,7 @@ Abre: **http://localhost:3000**
|
||||
| **Frontend** | 3000 | Interfaz web Vue |
|
||||
| **Backend** | 3001 | API Node.js |
|
||||
| **Redis** | 6379 | Cache de artículos |
|
||||
| **Wallamonitor** | - | Servicio Python (interno) |
|
||||
| **Wallabicher** | - | Servicio Python (interno) |
|
||||
|
||||
## 🔧 Comandos Básicos
|
||||
|
||||
@@ -56,7 +56,7 @@ Abre: **http://localhost:3000**
|
||||
docker-compose logs -f
|
||||
|
||||
# Ver logs de un servicio específico
|
||||
docker-compose logs -f wallamonitor
|
||||
docker-compose logs -f wallabicher
|
||||
|
||||
# Detener
|
||||
docker-compose down
|
||||
|
||||
@@ -2,7 +2,7 @@ services:
|
||||
# Redis para cache de artículos
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: wallamonitor-redis
|
||||
container_name: wallabicher-redis
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
@@ -14,7 +14,7 @@ services:
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
networks:
|
||||
- wallamonitor-network
|
||||
- wallabicher-network
|
||||
restart: unless-stopped
|
||||
|
||||
# Backend Node.js API
|
||||
@@ -22,7 +22,7 @@ services:
|
||||
build:
|
||||
context: ./web/backend
|
||||
dockerfile: Dockerfile
|
||||
container_name: wallamonitor-backend
|
||||
container_name: wallabicher-backend
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3001
|
||||
@@ -39,7 +39,7 @@ services:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- wallamonitor-network
|
||||
- wallabicher-network
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3001/api/stats"]
|
||||
@@ -52,24 +52,24 @@ services:
|
||||
build:
|
||||
context: ./web/frontend
|
||||
dockerfile: Dockerfile
|
||||
container_name: wallamonitor-frontend
|
||||
container_name: wallabicher-frontend
|
||||
depends_on:
|
||||
- backend
|
||||
networks:
|
||||
- wallamonitor-network
|
||||
- wallabicher-network
|
||||
restart: unless-stopped
|
||||
|
||||
# Servicio Python principal (Wallamonitor)
|
||||
# Servicio Python principal (Wallabicher)
|
||||
# NOTA: Para usar Redis, asegúrate de que config.yaml tenga:
|
||||
# cache:
|
||||
# type: "redis"
|
||||
# redis:
|
||||
# host: "redis" # Nombre del servicio en Docker
|
||||
wallamonitor:
|
||||
wallabicher:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: wallamonitor-python
|
||||
container_name: wallabicher-python
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
volumes:
|
||||
@@ -84,7 +84,7 @@ services:
|
||||
backend:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- wallamonitor-network
|
||||
- wallabicher-network
|
||||
restart: unless-stopped
|
||||
# El servicio Python no necesita exponer puertos, solo se comunica con Redis y Telegram
|
||||
|
||||
@@ -93,6 +93,6 @@ volumes:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
wallamonitor-network:
|
||||
wallabicher-network:
|
||||
driver: bridge
|
||||
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "wallamonitor",
|
||||
"name": "wallabicher",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script de inicialización de configuración para WallaMonitor.
|
||||
Script de inicialización de configuración para Wallabicher.
|
||||
|
||||
Este script copia los archivos de configuración de muestra (.sample)
|
||||
a sus ubicaciones finales si estos no existen.
|
||||
@@ -33,7 +33,7 @@ def setup_configuration():
|
||||
]
|
||||
|
||||
print("="*60)
|
||||
print(" WallaMonitor - Configuración Inicial")
|
||||
print(" Wallabicher - Configuración Inicial")
|
||||
print("="*60)
|
||||
print()
|
||||
|
||||
@@ -74,7 +74,7 @@ def setup_configuration():
|
||||
print(" - Ajusta las búsquedas según tus necesidades")
|
||||
print(" - Configura los thread_id para tus hilos de Telegram")
|
||||
print()
|
||||
print(" Luego ejecuta: python wallamonitor.py")
|
||||
print(" Luego ejecuta: python wallabicher.py")
|
||||
elif files_exist:
|
||||
print("\n✓ Todos los archivos de configuración ya existen.")
|
||||
print(" El sistema está listo para usar.")
|
||||
|
||||
@@ -61,7 +61,7 @@ La interfaz web lee automáticamente:
|
||||
## 🔧 Requisitos
|
||||
|
||||
- Node.js 18+
|
||||
- El sistema Python de Wallamonitor debe estar ejecutándose
|
||||
- El sistema Python de Wallabicher debe estar ejecutándose
|
||||
- Redis (opcional, pero recomendado)
|
||||
|
||||
## 📝 Notas
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 🎨 Wallamonitor Web Interface
|
||||
# 🎨 Wallabicher Web Interface
|
||||
|
||||
Interfaz web moderna para visualizar y gestionar tu sistema Wallamonitor. Construida con **Node.js**, **Express**, **Vue 3** y **Tailwind CSS**.
|
||||
Interfaz web moderna para visualizar y gestionar tu sistema Wallabicher. Construida con **Node.js**, **Express**, **Vue 3** y **Tailwind CSS**.
|
||||
|
||||
## 🚀 Características
|
||||
|
||||
@@ -15,7 +15,7 @@ Interfaz web moderna para visualizar y gestionar tu sistema Wallamonitor. Constr
|
||||
## 📋 Requisitos Previos
|
||||
|
||||
- Node.js 18+ y npm
|
||||
- El sistema Python de Wallamonitor ejecutándose
|
||||
- El sistema Python de Wallabicher ejecutándose
|
||||
- Redis (opcional, pero recomendado para mejor rendimiento)
|
||||
|
||||
## 🔧 Instalación
|
||||
|
||||
4
web/backend/package-lock.json
generated
4
web/backend/package-lock.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "wallamonitor-backend",
|
||||
"name": "wallabicher-backend",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wallamonitor-backend",
|
||||
"name": "wallabicher-backend",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "wallamonitor-backend",
|
||||
"name": "wallabicher-backend",
|
||||
"version": "1.0.0",
|
||||
"description": "Backend API para Wallamonitor Dashboard",
|
||||
"description": "Backend API para Wallabicher Dashboard",
|
||||
"main": "server.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"dev": "node --watch server.js"
|
||||
},
|
||||
"keywords": ["wallamonitor", "api", "express"],
|
||||
"keywords": ["wallabicher", "api", "express"],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Wallamonitor Dashboard</title>
|
||||
<title>Wallabicher Dashboard</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
4
web/frontend/package-lock.json
generated
4
web/frontend/package-lock.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "wallamonitor-frontend",
|
||||
"name": "wallabicher-frontend",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wallamonitor-frontend",
|
||||
"name": "wallabicher-frontend",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@heroicons/vue": "^2.1.1",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "wallamonitor-frontend",
|
||||
"name": "wallabicher-frontend",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
<div class="flex justify-between h-16">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0 flex items-center">
|
||||
<h1 class="text-2xl font-bold text-primary-600">🛎️ Wallamonitor</h1>
|
||||
<h1 class="text-xl sm:text-2xl font-bold text-primary-600">🛎️ Wallabicher</h1>
|
||||
</div>
|
||||
<div class="hidden sm:ml-6 sm:flex sm:space-x-8">
|
||||
<div class="hidden md:ml-6 md:flex md:space-x-8">
|
||||
<router-link
|
||||
v-for="item in navItems"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
class="inline-flex items-center px-1 pt-1 text-sm font-medium text-gray-900 hover:text-primary-600 border-b-2 border-transparent hover:border-primary-600"
|
||||
class="inline-flex items-center px-1 pt-1 text-sm font-medium text-gray-900 hover:text-primary-600 border-b-2 border-transparent hover:border-primary-600 transition-colors"
|
||||
active-class="border-primary-600 text-primary-600"
|
||||
>
|
||||
<component :is="item.icon" class="w-5 h-5 mr-2" />
|
||||
@@ -20,7 +20,45 @@
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="hidden sm:flex items-center space-x-2">
|
||||
<div
|
||||
class="w-3 h-3 rounded-full"
|
||||
:class="wsConnected ? 'bg-green-500' : 'bg-red-500'"
|
||||
></div>
|
||||
<span class="text-sm text-gray-600">
|
||||
{{ wsConnected ? 'Conectado' : 'Desconectado' }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- Mobile menu button -->
|
||||
<button
|
||||
@click="mobileMenuOpen = !mobileMenuOpen"
|
||||
class="md:hidden inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-primary-500"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<Bars3Icon v-if="!mobileMenuOpen" class="block h-6 w-6" />
|
||||
<XMarkIcon v-else class="block h-6 w-6" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile menu -->
|
||||
<div v-if="mobileMenuOpen" class="md:hidden border-t border-gray-200">
|
||||
<div class="pt-2 pb-3 space-y-1 px-4">
|
||||
<router-link
|
||||
v-for="item in navItems"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
@click="mobileMenuOpen = false"
|
||||
class="flex items-center px-3 py-2 text-base font-medium text-gray-900 hover:text-primary-600 hover:bg-gray-50 rounded-md transition-colors"
|
||||
:class="$route.path === item.path ? 'text-primary-600 bg-gray-50' : ''"
|
||||
>
|
||||
<component :is="item.icon" class="w-5 h-5 mr-3" />
|
||||
{{ item.name }}
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="pt-4 pb-3 border-t border-gray-200 px-4">
|
||||
<div class="flex items-center space-x-2">
|
||||
<div
|
||||
class="w-3 h-3 rounded-full"
|
||||
@@ -32,10 +70,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
|
||||
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 sm:py-6">
|
||||
<router-view />
|
||||
</main>
|
||||
</div>
|
||||
@@ -49,6 +86,8 @@ import {
|
||||
HeartIcon,
|
||||
Cog6ToothIcon,
|
||||
DocumentMagnifyingGlassIcon,
|
||||
Bars3Icon,
|
||||
XMarkIcon,
|
||||
} from '@heroicons/vue/24/outline';
|
||||
|
||||
const navItems = [
|
||||
@@ -60,6 +99,7 @@ const navItems = [
|
||||
];
|
||||
|
||||
const wsConnected = ref(false);
|
||||
const mobileMenuOpen = ref(false);
|
||||
let ws = null;
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -32,5 +32,13 @@
|
||||
.input {
|
||||
@apply w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent;
|
||||
}
|
||||
|
||||
/* Line clamp utility */
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="mb-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h1 class="text-3xl font-bold text-gray-900">Artículos Notificados</h1>
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="mb-4 sm:mb-6">
|
||||
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3 sm:gap-4 mb-4">
|
||||
<h1 class="text-2xl sm:text-3xl font-bold text-gray-900">Artículos Notificados</h1>
|
||||
<div class="flex flex-col sm:flex-row items-stretch sm:items-center gap-2 sm:space-x-4">
|
||||
<select
|
||||
v-model="selectedPlatform"
|
||||
@change="loadArticles"
|
||||
class="input"
|
||||
style="width: auto;"
|
||||
class="input text-sm sm:text-base"
|
||||
style="width: 100%; min-width: 180px;"
|
||||
>
|
||||
<option value="">Todas las plataformas</option>
|
||||
<option value="wallapop">Wallapop</option>
|
||||
<option value="vinted">Vinted</option>
|
||||
</select>
|
||||
<button @click="loadArticles" class="btn btn-primary">
|
||||
<button @click="loadArticles" class="btn btn-primary whitespace-nowrap">
|
||||
Actualizar
|
||||
</button>
|
||||
</div>
|
||||
@@ -63,18 +63,18 @@
|
||||
:key="`${article.platform}-${article.id}`"
|
||||
class="card hover:shadow-lg transition-shadow"
|
||||
>
|
||||
<div class="flex gap-4">
|
||||
<div class="flex flex-col sm:flex-row gap-3 sm:gap-4">
|
||||
<!-- Imagen del artículo -->
|
||||
<div class="flex-shrink-0">
|
||||
<div v-if="article.images && article.images.length > 0" class="w-32 h-32 relative">
|
||||
<div class="flex-shrink-0 self-center sm:self-start">
|
||||
<div v-if="article.images && article.images.length > 0" class="w-24 h-24 sm:w-32 sm:h-32 relative">
|
||||
<img
|
||||
:src="article.images[0]"
|
||||
:alt="article.title || 'Sin título'"
|
||||
class="w-32 h-32 object-cover rounded-lg"
|
||||
class="w-24 h-24 sm:w-32 sm:h-32 object-cover rounded-lg"
|
||||
@error="($event) => handleImageError($event)"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="w-32 h-32 bg-gray-200 rounded-lg flex items-center justify-center">
|
||||
<div v-else class="w-24 h-24 sm:w-32 sm:h-32 bg-gray-200 rounded-lg flex items-center justify-center">
|
||||
<span class="text-gray-400 text-xs">Sin imagen</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -83,7 +83,7 @@
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-start justify-between mb-2">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center space-x-2 mb-2">
|
||||
<div class="flex flex-wrap items-center gap-2 mb-2">
|
||||
<span
|
||||
class="px-2 py-1 text-xs font-semibold rounded flex-shrink-0"
|
||||
:class="
|
||||
@@ -94,12 +94,12 @@
|
||||
>
|
||||
{{ article.platform?.toUpperCase() || 'N/A' }}
|
||||
</span>
|
||||
<span class="text-sm text-gray-500 whitespace-nowrap">
|
||||
<span class="text-xs sm:text-sm text-gray-500">
|
||||
{{ formatDate(article.notifiedAt) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-1 truncate" :title="article.title">
|
||||
<h3 class="text-base sm:text-lg font-semibold text-gray-900 mb-1 line-clamp-2" :title="article.title">
|
||||
{{ article.title || 'Sin título' }}
|
||||
</h3>
|
||||
|
||||
@@ -109,36 +109,36 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1 text-sm text-gray-600 mb-2">
|
||||
<div v-if="article.location" class="flex items-center">
|
||||
<div class="space-y-1 text-xs sm:text-sm text-gray-600 mb-2">
|
||||
<div v-if="article.location" class="flex flex-wrap items-center">
|
||||
<span class="font-medium">📍 Localidad:</span>
|
||||
<span class="ml-2">{{ article.location }}</span>
|
||||
</div>
|
||||
<div v-if="article.allows_shipping !== null" class="flex items-center">
|
||||
<div v-if="article.allows_shipping !== null" class="flex flex-wrap items-center">
|
||||
<span class="font-medium">🚚 Envío:</span>
|
||||
<span class="ml-2">{{ article.allows_shipping ? '✅ Acepta envíos' : '❌ No acepta envíos' }}</span>
|
||||
</div>
|
||||
<div v-if="article.modified_at" class="flex items-center">
|
||||
<div v-if="article.modified_at" class="flex flex-wrap items-center">
|
||||
<span class="font-medium">🕒 Modificado:</span>
|
||||
<span class="ml-2">{{ article.modified_at }}</span>
|
||||
<span class="ml-2 break-all">{{ article.modified_at }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p v-if="article.description" class="text-sm text-gray-700 mb-2 overflow-hidden" style="display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;">
|
||||
<p v-if="article.description" class="text-xs sm:text-sm text-gray-700 mb-2 overflow-hidden line-clamp-2">
|
||||
{{ article.description }}
|
||||
</p>
|
||||
|
||||
<div class="flex items-center space-x-4 mt-3">
|
||||
<div class="flex flex-wrap items-center gap-2 sm:gap-4 mt-3">
|
||||
<a
|
||||
v-if="article.url"
|
||||
:href="article.url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-primary-600 hover:text-primary-700 text-sm font-medium"
|
||||
class="text-primary-600 hover:text-primary-700 text-xs sm:text-sm font-medium break-all"
|
||||
>
|
||||
🔗 Ver anuncio
|
||||
</a>
|
||||
<span class="text-xs text-gray-400">
|
||||
<span class="text-xs text-gray-400 break-all">
|
||||
ID: {{ article.id }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -159,14 +159,14 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="text-center text-sm text-gray-500 mt-4">
|
||||
<p class="text-center text-xs sm:text-sm text-gray-500 mt-4 px-2">
|
||||
<span v-if="searchQuery">
|
||||
Mostrando {{ filteredArticles.length }} resultados de búsqueda en Redis
|
||||
<span class="ml-2 text-xs text-primary-600">(de {{ total }} artículos totales)</span>
|
||||
<span class="block sm:inline sm:ml-2 text-xs text-primary-600">(de {{ total }} artículos totales)</span>
|
||||
</span>
|
||||
<span v-else>
|
||||
Mostrando {{ filteredArticles.length }} de {{ total }} artículos
|
||||
<span class="ml-2 text-xs text-gray-400">(Actualización automática cada 30s)</span>
|
||||
<span class="block sm:inline sm:ml-2 text-xs text-gray-400">(Actualización automática cada 30s)</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-gray-900 mb-6">Dashboard</h1>
|
||||
<h1 class="text-2xl sm:text-3xl font-bold text-gray-900 mb-4 sm:mb-6">Dashboard</h1>
|
||||
|
||||
<!-- Estadísticas -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-6 mb-6 sm:mb-8">
|
||||
<div class="card">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0 bg-primary-100 rounded-lg p-3">
|
||||
<Cog6ToothIcon class="w-6 h-6 text-primary-600" />
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-600">Workers Activos</p>
|
||||
<p class="text-2xl font-bold text-gray-900">{{ stats.activeWorkers }}/{{ stats.totalWorkers }}</p>
|
||||
<div class="ml-3 sm:ml-4">
|
||||
<p class="text-xs sm:text-sm font-medium text-gray-600">Workers Activos</p>
|
||||
<p class="text-xl sm:text-2xl font-bold text-gray-900">{{ stats.activeWorkers }}/{{ stats.totalWorkers }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -56,9 +56,9 @@
|
||||
</div>
|
||||
|
||||
<!-- Gráfico de plataformas -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4 sm:gap-6">
|
||||
<div class="card">
|
||||
<h2 class="text-xl font-bold text-gray-900 mb-4">Distribución por Plataforma</h2>
|
||||
<h2 class="text-lg sm:text-xl font-bold text-gray-900 mb-3 sm:mb-4">Distribución por Plataforma</h2>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<div class="flex justify-between mb-2">
|
||||
@@ -92,7 +92,7 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="text-xl font-bold text-gray-900 mb-4">Accesos Rápidos</h2>
|
||||
<h2 class="text-lg sm:text-xl font-bold text-gray-900 mb-3 sm:mb-4">Accesos Rápidos</h2>
|
||||
<div class="space-y-3">
|
||||
<router-link
|
||||
to="/articles"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h1 class="text-3xl font-bold text-gray-900">Favoritos</h1>
|
||||
<button @click="loadFavorites" class="btn btn-primary">
|
||||
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3 sm:gap-4 mb-4 sm:mb-6">
|
||||
<h1 class="text-2xl sm:text-3xl font-bold text-gray-900">Favoritos</h1>
|
||||
<button @click="loadFavorites" class="btn btn-primary self-start sm:self-auto">
|
||||
Actualizar
|
||||
</button>
|
||||
</div>
|
||||
@@ -57,17 +57,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex space-x-2 mt-4">
|
||||
<div class="flex flex-col sm:flex-row gap-2 sm:space-x-2 mt-4">
|
||||
<a
|
||||
:href="favorite.url"
|
||||
target="_blank"
|
||||
class="flex-1 btn btn-primary text-center"
|
||||
class="flex-1 btn btn-primary text-center text-sm sm:text-base"
|
||||
>
|
||||
Ver artículo
|
||||
</a>
|
||||
<button
|
||||
@click="removeFavorite(favorite.platform, favorite.id)"
|
||||
class="btn btn-danger"
|
||||
class="btn btn-danger text-sm sm:text-base"
|
||||
>
|
||||
Eliminar
|
||||
</button>
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h1 class="text-3xl font-bold text-gray-900">Logs del Sistema</h1>
|
||||
<div class="flex items-center space-x-4">
|
||||
<select v-model="logLevel" @change="loadLogs" class="input" style="width: auto;">
|
||||
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3 sm:gap-4 mb-4 sm:mb-6">
|
||||
<h1 class="text-2xl sm:text-3xl font-bold text-gray-900">Logs del Sistema</h1>
|
||||
<div class="flex flex-col sm:flex-row items-stretch sm:items-center gap-2 sm:space-x-4">
|
||||
<select v-model="logLevel" @change="loadLogs" class="input text-sm sm:text-base" style="width: 100%; min-width: 160px;">
|
||||
<option value="">Todos los niveles</option>
|
||||
<option value="INFO">INFO</option>
|
||||
<option value="WARNING">WARNING</option>
|
||||
<option value="ERROR">ERROR</option>
|
||||
<option value="DEBUG">DEBUG</option>
|
||||
</select>
|
||||
<button @click="loadLogs" class="btn btn-primary">
|
||||
<button @click="loadLogs" class="btn btn-primary text-sm sm:text-base whitespace-nowrap">
|
||||
Actualizar
|
||||
</button>
|
||||
<button @click="autoRefresh = !autoRefresh" class="btn btn-secondary">
|
||||
<button @click="autoRefresh = !autoRefresh" class="btn btn-secondary text-sm sm:text-base whitespace-nowrap">
|
||||
{{ autoRefresh ? '⏸ Pausar' : '▶ Auto-refresh' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="bg-gray-900 text-green-400 font-mono text-sm p-4 rounded-lg overflow-x-auto max-h-[600px] overflow-y-auto">
|
||||
<div class="card p-2 sm:p-6">
|
||||
<div class="bg-gray-900 text-green-400 font-mono text-xs sm:text-sm p-3 sm:p-4 rounded-lg overflow-x-auto max-h-[400px] sm:max-h-[600px] overflow-y-auto">
|
||||
<div v-if="loading" class="text-center py-8">
|
||||
<div class="inline-block animate-spin rounded-full h-6 w-6 border-b-2 border-green-400"></div>
|
||||
<p class="mt-2 text-gray-400">Cargando logs...</p>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h1 class="text-3xl font-bold text-gray-900">Gestión de Workers</h1>
|
||||
<div class="flex space-x-2">
|
||||
<button @click="showGeneralModal = true" class="btn btn-secondary">
|
||||
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3 sm:gap-4 mb-4 sm:mb-6">
|
||||
<h1 class="text-2xl sm:text-3xl font-bold text-gray-900">Gestión de Workers</h1>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button @click="showGeneralModal = true" class="btn btn-secondary text-xs sm:text-sm">
|
||||
⚙️ Configuración General
|
||||
</button>
|
||||
<button @click="handleClearCache" class="btn btn-secondary">
|
||||
<button @click="handleClearCache" class="btn btn-secondary text-xs sm:text-sm">
|
||||
🗑️ Limpiar Caché
|
||||
</button>
|
||||
<button @click="showAddModal = true" class="btn btn-primary">
|
||||
<button @click="showAddModal = true" class="btn btn-primary text-xs sm:text-sm whitespace-nowrap">
|
||||
+ Añadir Worker
|
||||
</button>
|
||||
</div>
|
||||
@@ -42,7 +42,7 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 text-sm mb-3">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4 text-xs sm:text-sm mb-3">
|
||||
<div>
|
||||
<span class="text-gray-600 block mb-1">Búsqueda:</span>
|
||||
<p class="font-medium">{{ worker.search_query }}</p>
|
||||
@@ -102,22 +102,22 @@
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex space-x-2 ml-4">
|
||||
<div class="flex flex-wrap gap-2 sm:space-x-2 sm:ml-4 mt-3 sm:mt-0">
|
||||
<button
|
||||
@click="editWorker(worker, activeWorkersIndex(index))"
|
||||
class="btn btn-secondary text-sm"
|
||||
class="btn btn-secondary text-xs sm:text-sm flex-1 sm:flex-none"
|
||||
>
|
||||
✏️ Editar
|
||||
</button>
|
||||
<button
|
||||
@click="deleteWorker(worker.name)"
|
||||
class="btn btn-danger text-sm"
|
||||
class="btn btn-danger text-xs sm:text-sm flex-1 sm:flex-none"
|
||||
>
|
||||
🗑️ Eliminar
|
||||
</button>
|
||||
<button
|
||||
@click="disableWorker(worker.name)"
|
||||
class="btn btn-secondary text-sm"
|
||||
class="btn btn-secondary text-xs sm:text-sm flex-1 sm:flex-none"
|
||||
>
|
||||
⏸️ Desactivar
|
||||
</button>
|
||||
@@ -128,18 +128,18 @@
|
||||
</div>
|
||||
|
||||
<!-- Workers desactivados -->
|
||||
<div v-if="disabledWorkers.length > 0" class="mt-8">
|
||||
<h2 class="text-xl font-semibold text-gray-900 mb-4">Workers Desactivados ({{ disabledWorkers.length }})</h2>
|
||||
<div v-if="disabledWorkers.length > 0" class="mt-6 sm:mt-8">
|
||||
<h2 class="text-lg sm:text-xl font-semibold text-gray-900 mb-3 sm:mb-4">Workers Desactivados ({{ disabledWorkers.length }})</h2>
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<div
|
||||
v-for="(worker, index) in disabledWorkers"
|
||||
:key="index"
|
||||
class="card opacity-60 hover:opacity-80 transition-opacity"
|
||||
>
|
||||
<div class="flex items-start justify-between">
|
||||
<div class="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center space-x-2 mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">{{ worker.name }}</h3>
|
||||
<div class="flex flex-wrap items-center gap-2 mb-2">
|
||||
<h3 class="text-base sm:text-lg font-semibold text-gray-900">{{ worker.name }}</h3>
|
||||
<span class="px-2 py-1 text-xs font-semibold rounded bg-red-100 text-red-800">
|
||||
Desactivado
|
||||
</span>
|
||||
@@ -147,24 +147,24 @@
|
||||
{{ (worker.platform || 'wallapop').toUpperCase() }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600">{{ worker.search_query }}</p>
|
||||
<p class="text-xs sm:text-sm text-gray-600">{{ worker.search_query }}</p>
|
||||
</div>
|
||||
<div class="flex space-x-2 ml-4">
|
||||
<div class="flex flex-wrap gap-2 sm:space-x-2 sm:ml-4">
|
||||
<button
|
||||
@click="editWorker(worker, disabledWorkersIndex(index))"
|
||||
class="btn btn-secondary text-sm"
|
||||
class="btn btn-secondary text-xs sm:text-sm flex-1 sm:flex-none"
|
||||
>
|
||||
✏️ Editar
|
||||
</button>
|
||||
<button
|
||||
@click="enableWorker(worker.name)"
|
||||
class="btn btn-primary text-sm"
|
||||
class="btn btn-primary text-xs sm:text-sm flex-1 sm:flex-none"
|
||||
>
|
||||
▶️ Activar
|
||||
</button>
|
||||
<button
|
||||
@click="deleteWorker(worker.name)"
|
||||
class="btn btn-danger text-sm"
|
||||
class="btn btn-danger text-xs sm:text-sm flex-1 sm:flex-none"
|
||||
>
|
||||
🗑️ Eliminar
|
||||
</button>
|
||||
@@ -185,18 +185,18 @@
|
||||
<!-- Modal para añadir/editar worker -->
|
||||
<div
|
||||
v-if="showAddModal || editingWorker"
|
||||
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
|
||||
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-2 sm:p-4"
|
||||
@click.self="closeModal"
|
||||
>
|
||||
<div class="bg-white rounded-lg p-6 max-w-4xl w-full mx-4 max-h-[90vh] overflow-y-auto">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4">
|
||||
<div class="bg-white rounded-lg p-4 sm:p-6 max-w-4xl w-full max-h-[95vh] sm:max-h-[90vh] overflow-y-auto">
|
||||
<h2 class="text-xl sm:text-2xl font-bold text-gray-900 mb-3 sm:mb-4">
|
||||
{{ editingWorker ? 'Editar Worker' : 'Añadir Worker' }}
|
||||
</h2>
|
||||
<form @submit.prevent="saveWorker" class="space-y-6">
|
||||
<!-- Información básica -->
|
||||
<div class="border-b border-gray-200 pb-4">
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-4">Información Básica</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="border-b border-gray-200 pb-3 sm:pb-4">
|
||||
<h3 class="text-base sm:text-lg font-semibold text-gray-900 mb-3 sm:mb-4">Información Básica</h3>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Nombre *</label>
|
||||
<input v-model="workerForm.name" type="text" class="input" required />
|
||||
@@ -216,9 +216,9 @@
|
||||
</div>
|
||||
|
||||
<!-- Precios y Thread -->
|
||||
<div class="border-b border-gray-200 pb-4">
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-4">Precios y Notificaciones</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class="border-b border-gray-200 pb-3 sm:pb-4">
|
||||
<h3 class="text-base sm:text-lg font-semibold text-gray-900 mb-3 sm:mb-4">Precios y Notificaciones</h3>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Precio Mínimo (€)</label>
|
||||
<input v-model.number="workerForm.min_price" type="number" class="input" min="0" step="0.01" />
|
||||
@@ -365,11 +365,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end space-x-2 pt-4">
|
||||
<button type="button" @click="closeModal" class="btn btn-secondary">
|
||||
<div class="flex flex-col-reverse sm:flex-row justify-end gap-2 sm:space-x-2 pt-4">
|
||||
<button type="button" @click="closeModal" class="btn btn-secondary text-sm sm:text-base">
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<button type="submit" class="btn btn-primary text-sm sm:text-base">
|
||||
Guardar
|
||||
</button>
|
||||
</div>
|
||||
@@ -380,11 +380,11 @@
|
||||
<!-- Modal para configuración general -->
|
||||
<div
|
||||
v-if="showGeneralModal"
|
||||
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
|
||||
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-2 sm:p-4"
|
||||
@click.self="closeGeneralModal"
|
||||
>
|
||||
<div class="bg-white rounded-lg p-6 max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4">Configuración General</h2>
|
||||
<div class="bg-white rounded-lg p-4 sm:p-6 max-w-2xl w-full max-h-[95vh] sm:max-h-[90vh] overflow-y-auto">
|
||||
<h2 class="text-xl sm:text-2xl font-bold text-gray-900 mb-3 sm:mb-4">Configuración General</h2>
|
||||
<p class="text-sm text-gray-600 mb-4">Estas configuraciones se aplican a todos los workers</p>
|
||||
<form @submit.prevent="saveGeneralConfig" class="space-y-4">
|
||||
<div>
|
||||
@@ -405,11 +405,11 @@
|
||||
placeholder="Una palabra por línea o separadas por comas"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="flex justify-end space-x-2 pt-4 border-t border-gray-200">
|
||||
<button type="button" @click="closeGeneralModal" class="btn btn-secondary">
|
||||
<div class="flex flex-col-reverse sm:flex-row justify-end gap-2 sm:space-x-2 pt-4 border-t border-gray-200">
|
||||
<button type="button" @click="closeGeneralModal" class="btn btn-secondary text-sm sm:text-base">
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<button type="submit" class="btn btn-primary text-sm sm:text-base">
|
||||
Guardar
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script para iniciar el servidor web de Wallamonitor
|
||||
# Script para iniciar el servidor web de Wallabicher
|
||||
|
||||
echo "🚀 Iniciando Wallamonitor Web Interface..."
|
||||
echo "🚀 Iniciando Wallabicher Web Interface..."
|
||||
echo ""
|
||||
|
||||
# Verificar que Node.js esté instalado
|
||||
|
||||
Reference in New Issue
Block a user