Compare commits
9 Commits
e354433cd6
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d84b5c0853 | |||
| 56819fb1ca | |||
| f96a358227 | |||
| 66fdf8bb3b | |||
| d9196512de | |||
| 5129b517d8 | |||
| 85aa6a38ba | |||
| 7c801b4b73 | |||
| 06d95d32ca |
BIN
Manual Titulares APP.pdf
Normal file
BIN
Manual Titulares APP.pdf
Normal file
Binary file not shown.
180
README.md
Normal file
180
README.md
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
## 📰 Titulares APP
|
||||||
|
---
|
||||||
|
**Titulares APP** es una aplicación web de dashboard en tiempo real diseñada para extraer titulares de un sitio de noticias, gestionarlos y exportarlos automáticamente a un archivo CSV en una ubicación de red.
|
||||||
|
|
||||||
|
El sistema se compone de un **backend RESTful API desarrollado en ASP.NET Core** que gestiona el scraping, la base de datos y la comunicación en tiempo real, y un **frontend interactivo de tipo SPA (Single Page Application) construido con React, TypeScript y Vite**.
|
||||||
|
|
||||||
|
---
|
||||||
|
## 🚀 Funcionalidades Principales
|
||||||
|
|
||||||
|
El sistema está diseñado para la automatización, la gestión centralizada y la visualización en tiempo real de titulares de noticias.
|
||||||
|
|
||||||
|
### 🤖 Motor de Scraping Automatizado
|
||||||
|
- **Worker en Segundo Plano:** Un proceso `BackgroundService` de .NET se ejecuta de forma continua en el servidor.
|
||||||
|
- **Scraping Periódico:** Extrae los últimos titulares del sitio de noticias (`eldia.com`) a intervalos configurables.
|
||||||
|
- **Sincronización Inteligente:** Compara los titulares extraídos con los existentes en la base de datos para evitar duplicados y mantener la lista actualizada, preservando el orden de los titulares manuales.
|
||||||
|
- **Limpieza de Datos:** Procesa el texto de los titulares para eliminar prefijos no deseados (ej: "VIDEO.-") y decodificar entidades HTML.
|
||||||
|
|
||||||
|
### ⚡ Actualizaciones en Tiempo Real con SignalR
|
||||||
|
- **Notificación Instantánea:** Cualquier cambio en la lista de titulares (por scraping, creación manual, edición, reordenamiento o eliminación) es notificado instantáneamente a todos los clientes conectados.
|
||||||
|
- **Dashboard Sincronizado:** La tabla de titulares en la interfaz de todos los usuarios se actualiza en tiempo real sin necesidad de refrescar la página.
|
||||||
|
- **Gestión de Conexión:** El backend detecta cuándo hay clientes activos y puede pausar el proceso de scraping si no hay nadie conectado para ahorrar recursos.
|
||||||
|
|
||||||
|
### 🖐️ Gestión Manual Completa
|
||||||
|
- **Creación de Titulares:** Permite añadir titulares manualmente, los cuales se insertan al principio de la lista.
|
||||||
|
- **Edición Rápida:**
|
||||||
|
- Edición del texto del titular directamente en la tabla.
|
||||||
|
- Edición de la viñeta (bullet point) a través de un modal dedicado.
|
||||||
|
- **Reordenamiento Drag & Drop:** Permite cambiar el orden visual de los titulares simplemente arrastrándolos y soltándolos en la tabla.
|
||||||
|
- **Eliminación Segura:** Borrado de titulares con un modal de confirmación para evitar acciones accidentales.
|
||||||
|
|
||||||
|
### ⚙️ Panel de Configuración Dinámico
|
||||||
|
- **Gestión Centralizada:** Una sección en la UI permite configurar todos los parámetros clave del sistema.
|
||||||
|
- **Parámetros Configurables:**
|
||||||
|
- **Ruta del archivo CSV:** Ubicación de red donde se guardará el archivo.
|
||||||
|
- **Intervalo de Scraping:** Frecuencia en minutos para la extracción de nuevos titulares.
|
||||||
|
- **Cantidad de Titulares:** Número máximo de titulares a mantener en la lista.
|
||||||
|
- **Viñeta por Defecto:** Símbolo a usar cuando un titular no tiene una viñeta específica.
|
||||||
|
- **Auto-Guardado Inteligente:** Los cambios en la configuración se guardan automáticamente en la base de datos después de una breve pausa (`debounce`), proporcionando una experiencia de usuario fluida.
|
||||||
|
|
||||||
|
### 📄 Exportación a CSV Robusta
|
||||||
|
- **Generación Automática:** Después de cada ciclo de scraping exitoso, el sistema regenera automáticamente el archivo `titulares.csv` en la ruta de red configurada.
|
||||||
|
- **Generación Manual:** Un botón en la UI permite forzar la regeneración del archivo CSV en cualquier momento.
|
||||||
|
- **Codificación Específica:** El archivo CSV se genera con codificación `UTF-16 Big Endian con BOM` para máxima compatibilidad con sistemas específicos que puedan consumir el archivo.
|
||||||
|
|
||||||
|
### 🎨 Interfaz de Usuario Moderna
|
||||||
|
- **Diseño Oscuro y Profesional:** Construido con Material-UI, ofreciendo una experiencia visual agradable y consistente.
|
||||||
|
- **Feedback al Usuario:** Notificaciones instantáneas para operaciones de éxito o error.
|
||||||
|
- **Indicadores de Estado:** Muestra el estado de la conexión con el servidor (Conectado, Reconectando, Desconectado) y el estado del proceso de scraping (ON/OFF).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Stack Tecnológico
|
||||||
|
|
||||||
|
### Backend (`backend/`)
|
||||||
|
- **Framework:** ASP.NET Core 9
|
||||||
|
- **Lenguaje:** C#
|
||||||
|
- **Acceso a Datos:** Dapper (Micro ORM)
|
||||||
|
- **Base de Datos:** Microsoft SQL Server
|
||||||
|
- **Scraping:** HtmlAgilityPack
|
||||||
|
- **Comunicación Real-Time:** SignalR
|
||||||
|
- **Generación CSV:** CsvHelper
|
||||||
|
|
||||||
|
### Frontend (`frontend/`)
|
||||||
|
- **Librería:** React 19
|
||||||
|
- **Lenguaje:** TypeScript
|
||||||
|
- **Componentes UI:** Material-UI (MUI)
|
||||||
|
- **Drag & Drop:** dnd-kit
|
||||||
|
- **Comunicación Real-Time:** Cliente de SignalR (`@microsoft/signalr`)
|
||||||
|
- **Build Tool:** Vite
|
||||||
|
|
||||||
|
### Entorno de Producción
|
||||||
|
- **Contenerización:** Docker & Docker Compose
|
||||||
|
- **Proxy Inverso y Servidor Web:** Nginx (configurado para servir React y actuar como proxy para la API y SignalR/WebSockets)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Puesta en Marcha (Getting Started)
|
||||||
|
|
||||||
|
Siga estos pasos para configurar y ejecutar el proyecto.
|
||||||
|
|
||||||
|
### 1. Entorno de Desarrollo Local
|
||||||
|
|
||||||
|
#### Prerrequisitos
|
||||||
|
- **.NET SDK 9.0** o superior.
|
||||||
|
- **Node.js** v20.x o superior.
|
||||||
|
- **Microsoft SQL Server** y una herramienta de gestión como SSMS.
|
||||||
|
|
||||||
|
#### Configuración
|
||||||
|
1. **Clonar el Repositorio:**
|
||||||
|
```bash
|
||||||
|
git clone <URL_DEL_REPOSITORIO_GITEA>
|
||||||
|
cd TitularesApp # O el nombre de tu carpeta
|
||||||
|
```
|
||||||
|
2. **Base de Datos:**
|
||||||
|
- Cree una base de datos en su instancia de SQL Server (ej: `TitularesDB`).
|
||||||
|
- Ejecute los scripts necesarios para crear las tablas `Titulares` y `Configuracion` según los modelos en el proyecto (`Titular.cs`, `ConfiguracionApp.cs`).
|
||||||
|
3. **Backend (`backend/src/Titulares.Api`):**
|
||||||
|
- Renombre o copie `appsettings.Development.json` si es necesario.
|
||||||
|
- **Modifique la `ConnectionString`** en `appsettings.json` para apuntar a su base de datos.
|
||||||
|
- Ejecute desde el directorio `backend/src/Titulares.Api`:
|
||||||
|
```bash
|
||||||
|
dotnet restore
|
||||||
|
dotnet run
|
||||||
|
```
|
||||||
|
- La API estará escuchando en `http://localhost:5174`.
|
||||||
|
4. **Frontend (`frontend/`):**
|
||||||
|
- Verifique que `frontend/.env.development` contiene `VITE_API_BASE_URL=http://localhost:5174`.
|
||||||
|
- Ejecute desde el directorio `frontend/`:
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
- La aplicación estará disponible en `http://localhost:5173`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Entorno de Producción con Docker
|
||||||
|
|
||||||
|
Esta es la forma recomendada de desplegar la aplicación.
|
||||||
|
|
||||||
|
#### Prerrequisitos
|
||||||
|
- **Docker** y **Docker Compose** instalados en el servidor anfitrión (ej: `192.168.5.128`).
|
||||||
|
- Una **carpeta compartida en la red** accesible desde el servidor Docker, donde se guardará el archivo CSV.
|
||||||
|
|
||||||
|
#### Configuración del Servidor Anfitrión (Host de Docker)
|
||||||
|
Para asegurar que el contenedor pueda escribir en la carpeta de red de forma persistente y resiliente a reinicios, se recomienda configurar un montaje bajo demanda con `autofs`.
|
||||||
|
|
||||||
|
1. **Instalar dependencias:** `sudo apt-get install -y autofs cifs-utils`.
|
||||||
|
2. **Configurar el mapa maestro `auto.master`:** Añadir la línea `/mnt /etc/auto.cifs --timeout=60`.
|
||||||
|
3. **Crear el mapa `auto.cifs`** con los detalles de la carpeta compartida, apuntando a un archivo de credenciales seguro.
|
||||||
|
```
|
||||||
|
# /etc/auto.cifs
|
||||||
|
nombre_montaje -fstype=cifs,credentials=/ruta/segura/credenciales.txt,uid=1000,gid=1000,vers=3.0 ://IP_PC_USUARIO/CARPETA_COMPARTIDA
|
||||||
|
```
|
||||||
|
4. **Reiniciar el servicio:** `sudo systemctl restart autofs`.
|
||||||
|
|
||||||
|
#### Despliegue
|
||||||
|
1. **Clonar el repositorio** en el servidor Docker.
|
||||||
|
2. **Configurar la red externa de Docker** si es necesario (para la base de datos).
|
||||||
|
```bash
|
||||||
|
docker network create shared-net
|
||||||
|
```
|
||||||
|
3. **Ejecutar Docker Compose** desde la raíz del proyecto.
|
||||||
|
```bash
|
||||||
|
docker-compose up --build -d
|
||||||
|
```
|
||||||
|
4. **Acceder a la Aplicación:** La aplicación estará disponible en la IP del servidor Docker y el puerto configurado en `docker-compose.yml`.
|
||||||
|
- **URL:** `http://192.168.5.128:8905`
|
||||||
|
|
||||||
|
---
|
||||||
|
## 📂 Estructura del Proyecto
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── backend/
|
||||||
|
│ └── src/
|
||||||
|
│ └── Titulares.Api/ # Proyecto principal de ASP.NET Core
|
||||||
|
│ ├── Controllers/ # Controladores de la API
|
||||||
|
│ ├── Data/ # Repositorios (Dapper) para acceso a datos
|
||||||
|
│ ├── Hubs/ # Hubs de SignalR
|
||||||
|
│ ├── Models/ # Clases de modelo y DTOs
|
||||||
|
│ ├── Services/ # Lógica de negocio (Scraping, CSV, Estado)
|
||||||
|
│ ├── Workers/ # Servicios en segundo plano
|
||||||
|
│ ├── Dockerfile # Instrucciones para construir la imagen del backend
|
||||||
|
│ └── ...
|
||||||
|
├── frontend/
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── components/ # Componentes de React
|
||||||
|
│ │ ├── contexts/ # Proveedores de Contexto (Notificaciones)
|
||||||
|
│ │ ├── hooks/ # Hooks personalizados (useSignalR, useDebounce)
|
||||||
|
│ │ ├── services/ # Lógica de llamadas a la API (axios)
|
||||||
|
│ │ ├── App.tsx # Componente principal de la aplicación
|
||||||
|
│ │ └── main.tsx # Punto de entrada de React
|
||||||
|
│ ├── .env.development # Variables de entorno para desarrollo
|
||||||
|
│ ├── .env.production # Variables de entorno para producción
|
||||||
|
│ └── Dockerfile # Instrucciones para construir la imagen del frontend
|
||||||
|
├── nginx/
|
||||||
|
│ └── nginx.conf # Configuración de Nginx como proxy inverso
|
||||||
|
└── docker-compose.yml # Orquestación de los contenedores
|
||||||
|
```
|
||||||
26
backend/src/Titulares.Api/Dockerfile
Normal file
26
backend/src/Titulares.Api/Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Etapa 1: Compilación
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
# 1. Copia solo el archivo .csproj.
|
||||||
|
# La ruta de origen es "Titulares.Api.csproj" porque está en la raíz del contexto.
|
||||||
|
COPY Titulares.Api.csproj .
|
||||||
|
|
||||||
|
# 2. Restaura las dependencias. Docker guardará esta capa en caché.
|
||||||
|
RUN dotnet restore "Titulares.Api.csproj"
|
||||||
|
|
||||||
|
# 3. Copia el resto de los archivos del proyecto.
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 4. Publica la aplicación.
|
||||||
|
# --no-restore se usa porque ya hemos restaurado las dependencias.
|
||||||
|
RUN dotnet publish "Titulares.Api.csproj" -c Release -o /app/publish --no-restore
|
||||||
|
|
||||||
|
# Etapa 2: Imagen Final
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app/publish .
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
ENV ASPNETCORE_ENVIRONMENT=Production
|
||||||
|
ENTRYPOINT ["dotnet", "Titulares.Api.dll"]
|
||||||
@@ -23,12 +23,15 @@ builder.Services.AddScoped<CsvService>();
|
|||||||
builder.Services.AddSingleton<ConfiguracionRepositorio>();
|
builder.Services.AddSingleton<ConfiguracionRepositorio>();
|
||||||
builder.Services.AddSingleton<EstadoProcesoService>();
|
builder.Services.AddSingleton<EstadoProcesoService>();
|
||||||
|
|
||||||
|
// Obtener los orígenes permitidos desde la configuración
|
||||||
|
var allowedOrigins = builder.Configuration.GetValue<string>("AllowedOrigins")?.Split(',') ?? new[] { "http://localhost:5173" };
|
||||||
|
|
||||||
// Añadimos la política de CORS
|
// Añadimos la política de CORS
|
||||||
builder.Services.AddCors(options =>
|
builder.Services.AddCors(options =>
|
||||||
{
|
{
|
||||||
options.AddPolicy("AllowReactApp", builder =>
|
options.AddPolicy("AllowReactApp", policyBuilder =>
|
||||||
{
|
{
|
||||||
builder.WithOrigins("http://localhost:5173")
|
policyBuilder.WithOrigins(allowedOrigins)
|
||||||
.AllowAnyHeader()
|
.AllowAnyHeader()
|
||||||
.AllowAnyMethod()
|
.AllowAnyMethod()
|
||||||
.AllowCredentials();
|
.AllowCredentials();
|
||||||
|
|||||||
11
backend/src/Titulares.Api/appsettings.Production.json
Normal file
11
backend/src/Titulares.Api/appsettings.Production.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"DefaultConnection": "Server=db-sqlserver;Database=TitularesDB;User Id=titularesApi;Password=PTP847Titulares;TrustServerCertificate=True;"
|
||||||
|
},
|
||||||
|
"AllowedOrigins": "http://192.168.5.128:8905",
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
|
"AllowedOrigins": "http://localhost:5173",
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"DefaultConnection": "Server=TECNICA3;Database=TitularesDB;User Id=titularesApi;Password=PTP847Titulares;Trusted_Connection=True;TrustServerCertificate=True;"
|
"DefaultConnection": "Server=TECNICA3;Database=TitularesDB;User Id=titularesApi;Password=PTP847Titulares;Trusted_Connection=True;TrustServerCertificate=True;"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"RutaCsv": "C:\\temp\\titulares.csv",
|
"RutaCsv": "/data/titulares.csv",
|
||||||
"IntervaloMinutos": 15,
|
"IntervaloMinutos": 15,
|
||||||
"CantidadTitularesAScrapear": 4,
|
"CantidadTitularesAScrapear": 4,
|
||||||
"ScrapingActivo": false
|
"ScrapingActivo": false
|
||||||
|
|||||||
46
docker-compose.yml
Normal file
46
docker-compose.yml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
services:
|
||||||
|
# Servicio del Backend
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: ./backend/src/Titulares.Api
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: titulares-api
|
||||||
|
# No exponemos puertos al exterior, el proxy se encarga de eso.
|
||||||
|
environment:
|
||||||
|
# Le decimos a ASP.NET Core que escuche en el puerto 8080 en todas las interfaces de red
|
||||||
|
- ASPNETCORE_URLS=http://+:8080
|
||||||
|
volumes:
|
||||||
|
# Mapea la carpeta del host (que será gestionada por autofs)
|
||||||
|
# a una carpeta '/data' dentro de este contenedor.
|
||||||
|
# La aplicación .NET ahora solo necesita escribir en '/data/titulares.csv'.
|
||||||
|
- /mnt/autofs/titulares_csv_remoto:/data
|
||||||
|
networks:
|
||||||
|
- app-net
|
||||||
|
- shared-net # Conectamos a la red externa para la DB
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# Servicio de Nginx (Proxy Inverso + Servidor Frontend)
|
||||||
|
nginx-proxy:
|
||||||
|
build:
|
||||||
|
context: ./frontend # Construimos desde la carpeta del frontend...
|
||||||
|
dockerfile: Dockerfile # ...usando su Dockerfile para generar los estáticos
|
||||||
|
container_name: titulares-proxy
|
||||||
|
# Montamos nuestra configuración personalizada de Nginx
|
||||||
|
volumes:
|
||||||
|
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
|
||||||
|
ports:
|
||||||
|
# Mapeamos el puerto 8905 del host al puerto 80 del contenedor Nginx
|
||||||
|
- "8905:80"
|
||||||
|
networks:
|
||||||
|
- app-net
|
||||||
|
depends_on:
|
||||||
|
- backend # Aseguramos que el backend se inicie antes que el proxy
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
networks:
|
||||||
|
# Red interna para la comunicación entre el proxy y el backend
|
||||||
|
app-net:
|
||||||
|
driver: bridge
|
||||||
|
# Definimos la red compartida como externa, ya que fue creada por otro stack
|
||||||
|
shared-net:
|
||||||
|
external: true
|
||||||
1
frontend/.env.development
Normal file
1
frontend/.env.development
Normal file
@@ -0,0 +1 @@
|
|||||||
|
VITE_API_BASE_URL=http://localhost:5174
|
||||||
1
frontend/.env.production
Normal file
1
frontend/.env.production
Normal file
@@ -0,0 +1 @@
|
|||||||
|
VITE_API_BASE_URL=
|
||||||
30
frontend/Dockerfile
Normal file
30
frontend/Dockerfile
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Etapa 1: Construcción de la aplicación React
|
||||||
|
# Usamos una imagen de Node.js (versión Alpine por ser ligera)
|
||||||
|
FROM node:20-alpine AS build
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copiamos package.json y package-lock.json para instalar dependencias
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Copiamos el resto de los archivos del frontend
|
||||||
|
COPY . .
|
||||||
|
# Construimos la aplicación para producción. Los archivos resultantes irán a /app/dist
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Etapa 2: Servidor Nginx
|
||||||
|
# Usamos la imagen oficial de Nginx (versión Alpine por ser ligera)
|
||||||
|
FROM nginx:stable-alpine
|
||||||
|
WORKDIR /usr/share/nginx/html
|
||||||
|
|
||||||
|
# Eliminamos el contenido por defecto de Nginx
|
||||||
|
RUN rm -rf ./*
|
||||||
|
|
||||||
|
# Copiamos los archivos estáticos construidos en la etapa anterior
|
||||||
|
COPY --from=build /app/dist .
|
||||||
|
|
||||||
|
# Exponemos el puerto 80, que es el puerto por defecto de Nginx
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
# El comando por defecto de la imagen de Nginx es suficiente para iniciar el servidor
|
||||||
|
# CMD ["nginx", "-g", "daemon off;"]
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/elDia.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>frontend</title>
|
<title>Titulares APP</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
BIN
frontend/public/elDia.ico
Normal file
BIN
frontend/public/elDia.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
@@ -3,7 +3,10 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import * as signalR from '@microsoft/signalr';
|
import * as signalR from '@microsoft/signalr';
|
||||||
|
|
||||||
const HUB_URL = 'http://localhost:5174/titularesHub';
|
// La URL base viene de la variable de entorno
|
||||||
|
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '';
|
||||||
|
// Construimos la URL completa del Hub
|
||||||
|
const HUB_URL = `${API_BASE_URL}/titularesHub`;
|
||||||
|
|
||||||
// Definimos un tipo para el estado de la conexión para más claridad
|
// Definimos un tipo para el estado de la conexión para más claridad
|
||||||
export type ConnectionStatus = 'Connecting' | 'Connected' | 'Disconnected' | 'Reconnecting';
|
export type ConnectionStatus = 'Connecting' | 'Connected' | 'Disconnected' | 'Reconnecting';
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import type { Configuracion, Titular } from '../types';
|
import type { Configuracion, Titular } from '../types';
|
||||||
|
|
||||||
const API_URL = 'http://localhost:5174/api';
|
// La URL base viene de la variable de entorno
|
||||||
|
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '';
|
||||||
|
|
||||||
const apiClient = axios.create({
|
const apiClient = axios.create({
|
||||||
baseURL: API_URL,
|
// Construimos la URL completa para las llamadas a la API REST
|
||||||
|
baseURL: `${API_BASE_URL}/api`,
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
46
nginx/nginx.conf
Normal file
46
nginx/nginx.conf
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# Define un "upstream" que apunta a nuestro servicio de backend.
|
||||||
|
# 'backend' será el nombre del servicio en docker-compose, y 8080 es el puerto
|
||||||
|
# que expusimos en su Dockerfile.
|
||||||
|
upstream backend_api {
|
||||||
|
server backend:8080;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
# Nginx escuchará en el puerto 80 DENTRO del contenedor.
|
||||||
|
listen 80;
|
||||||
|
server_name 192.168.5.128; # Opcional, puedes usar localhost o tu dominio
|
||||||
|
|
||||||
|
# Ubicación raíz: sirve los archivos de la aplicación React.
|
||||||
|
location / {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
try_files $uri /index.html; # Clave para Single Page Applications (SPA)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ubicación para la API REST.
|
||||||
|
# Todas las peticiones a http://.../api/... serán redirigidas al backend.
|
||||||
|
location /api {
|
||||||
|
proxy_pass http://backend_api;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ----------- CONFIGURACIÓN CRÍTICA PARA SIGNALR -----------
|
||||||
|
# Ubicación para el Hub de SignalR.
|
||||||
|
location /titularesHub {
|
||||||
|
proxy_pass http://backend_api;
|
||||||
|
|
||||||
|
# Requerido para que la conexión WebSocket funcione
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "Upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
|
||||||
|
# Aumenta el timeout para conexiones de larga duración
|
||||||
|
proxy_read_timeout 90s;
|
||||||
|
|
||||||
|
# Evita que el proxy almacene en caché la negociación de WebSockets
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user