diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2e3a6f2 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,63 @@ +services: + # Servicio del Backend API + mercados-api: + build: + context: ./MercadosModernos # Asumiendo que clonaste el repo en esta carpeta + dockerfile: src/Mercados.Api/Dockerfile + container_name: mercados-api + restart: always + env_file: + - ./.env # Lee las variables desde un archivo .env en la misma carpeta + networks: + - mercados-net + - shared-net # Se conecta a la red compartida para hablar con la DB + # NO se exponen puertos al host. + + # Servicio del Worker + mercados-worker: + build: + context: ./MercadosModernos + dockerfile: src/Mercados.Worker/Dockerfile + container_name: mercados-worker + restart: always + env_file: + - ./.env + networks: + - shared-net # Solo necesita acceso a la DB. + # NO se exponen puertos al host. + + # Servicio del Frontend (servido por Nginx) + mercados-frontend: + build: + context: ./MercadosModernos/frontend + dockerfile: Dockerfile + container_name: mercados-frontend + restart: always + networks: + - mercados-net + # NO se exponen puertos al host. + + # --- NUEVO SERVICIO: Proxy Inverso Local --- + proxy: + image: nginx:1.25-alpine + container_name: mercados-proxy + restart: always + volumes: + # Mapeamos nuestro archivo de configuración al contenedor de Nginx + - ./proxy-local/nginx.conf:/etc/nginx/conf.d/default.conf + ports: + # ESTE ES EL ÚNICO PUNTO DE ENTRADA DESDE EL EXTERIOR + # Expone el puerto 80 del contenedor al puerto 8500 del host Debian. + - "8500:80" + networks: + - mercados-net + depends_on: + - mercados-api + - mercados-frontend + +networks: + mercados-net: + driver: bridge + + shared-net: + external: true \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..8790e2e --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,28 @@ +# --- Etapa 1: Build --- +# Usamos una imagen ligera de Node.js para instalar dependencias y compilar el frontend +FROM node:20-alpine AS build +WORKDIR /app + +# Copiamos package.json y package-lock.json para cachear las dependencias +COPY package*.json ./ +RUN npm install + +# Copiamos el resto del código del frontend +COPY . . +# Ejecutamos el script de build de Vite +RUN npm run build + +# --- Etapa 2: Producción --- +# Usamos la imagen oficial de Nginx, que es muy pequeña y eficiente +FROM nginx:1.25-alpine + +# Copiamos los archivos estáticos generados en la etapa de build +# a la carpeta que Nginx sirve por defecto. +COPY --from=build /app/dist /usr/share/nginx/html + +# Nginx ya está configurado para escuchar en el puerto 80 por defecto. +# Simplemente lo exponemos. +EXPOSE 80 + +# El comando por defecto de la imagen de Nginx ya inicia el servidor. +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..dcf7994 --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,25 @@ +server { + listen 80; + server_name widgets.eldia.com; + + # Ubicación raíz para servir los archivos estáticos del frontend + root /usr/share/nginx/html; + index index.html; + + # Configuración para las rutas del frontend + location / { + try_files $uri $uri/ /index.html; + } + + # --- Proxy Inverso para la API --- + # Todas las peticiones que empiecen con /api/ se redirigen al backend + location /api/ { + # El nombre 'backend-api' debe coincidir con el nombre del servicio en docker-compose.yml + # El puerto 8080 es el puerto que expone el contenedor de la API (no el host) + proxy_pass http://backend-api:8080; + 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; + } +} \ No newline at end of file diff --git a/frontend/src/components/raw-data/RawBolsaUsaTable.tsx b/frontend/src/components/raw-data/RawBolsaUsaTable.tsx index 2514887..43d828c 100644 --- a/frontend/src/components/raw-data/RawBolsaUsaTable.tsx +++ b/frontend/src/components/raw-data/RawBolsaUsaTable.tsx @@ -62,8 +62,8 @@ export const RawBolsaUsaTable = () => { {row.ticker} {row.nombreEmpresa} - ${formatCurrency(row.precioActual, 'USD')} - ${formatCurrency(row.cierreAnterior, 'USD')} + {formatCurrency(row.precioActual, 'USD')} + {formatCurrency(row.cierreAnterior, 'USD')} {row.porcentajeCambio.toFixed(2)}% {formatFullDateTime(row.fechaRegistro)} diff --git a/src/Mercados.Api/Dockerfile b/src/Mercados.Api/Dockerfile new file mode 100644 index 0000000..58dff8e --- /dev/null +++ b/src/Mercados.Api/Dockerfile @@ -0,0 +1,27 @@ +# --- Etapa 1: Build --- +# Usamos la imagen del SDK de .NET 8 para compilar la aplicación +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +WORKDIR /src + +# Copiamos los archivos .csproj de cada proyecto para restaurar las dependencias de forma eficiente +COPY ["src/Mercados.Api/Mercados.Api.csproj", "Mercados.Api/"] +COPY ["src/Mercados.Infrastructure/Mercados.Infrastructure.csproj", "Mercados.Infrastructure/"] +COPY ["src/Mercados.Core/Mercados.Core.csproj", "Mercados.Core/"] +COPY ["src/Mercados.Database/Mercados.Database.csproj", "Mercados.Database/"] +RUN dotnet restore "Mercados.Api/Mercados.Api.csproj" + +# Copiamos el resto del código fuente +COPY src/. . + +# Publicamos la aplicación en modo Release, optimizada para producción +WORKDIR "/src/Mercados.Api" +RUN dotnet publish "Mercados.Api.csproj" -c Release -o /app/publish + +# --- Etapa 2: Final --- +# Usamos la imagen de runtime de ASP.NET, que es mucho más ligera que la del SDK +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final +WORKDIR /app +COPY --from=build /app/publish . + +# Definimos el punto de entrada para ejecutar la aplicación cuando el contenedor arranque +ENTRYPOINT ["dotnet", "Mercados.Api.dll"] \ No newline at end of file diff --git a/src/Mercados.Infrastructure/DataFetchers/FinnhubDataFetcher.cs b/src/Mercados.Infrastructure/DataFetchers/FinnhubDataFetcher.cs index b3c2b2b..5717160 100644 --- a/src/Mercados.Infrastructure/DataFetchers/FinnhubDataFetcher.cs +++ b/src/Mercados.Infrastructure/DataFetchers/FinnhubDataFetcher.cs @@ -16,7 +16,7 @@ namespace Mercados.Infrastructure.DataFetchers // Empresas 'Latinas' en Wall Street "MELI", "GLOB", // ADRs Argentinos - "YPF", "GGAL", "BMA", "LOMA", "PAM", "TEO", "TGS", "EDN", "CRESY", "CEPU", "BBAR" + "YPF", "GGAL", "BMA", "LOMA", "TEO", "TGS", "EDN", "BBAR" }; private readonly FinnhubClient _client; diff --git a/src/Mercados.Worker/Dockerfile b/src/Mercados.Worker/Dockerfile new file mode 100644 index 0000000..3b1d64b --- /dev/null +++ b/src/Mercados.Worker/Dockerfile @@ -0,0 +1,23 @@ +# --- Etapa 1: Build --- +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +WORKDIR /src + +# Copiamos los archivos .csproj y restauramos +COPY ["src/Mercados.Worker/Mercados.Worker.csproj", "Mercados.Worker/"] +COPY ["src/Mercados.Infrastructure/Mercados.Infrastructure.csproj", "Mercados.Infrastructure/"] +COPY ["src/Mercados.Core/Mercados.Core.csproj", "Mercados.Core/"] +RUN dotnet restore "Mercados.Worker/Mercados.Worker.csproj" + +# Copiamos el resto del código +COPY src/. . + +# Publicamos la aplicación +WORKDIR "/src/Mercados.Worker" +RUN dotnet publish "Mercados.Worker.csproj" -c Release -o /app/publish + +# --- Etapa 2: Final --- +# Usamos la imagen de runtime genérica de .NET, no la de ASP.NET +FROM mcr.microsoft.com/dotnet/runtime:9.0 AS final +WORKDIR /app +COPY --from=build /app/publish . +ENTRYPOINT ["dotnet", "Mercados.Worker.dll"] \ No newline at end of file