Deat Dockerfiles y Fix Base_URL

This commit is contained in:
2025-10-07 15:18:11 -03:00
parent 04f1134be4
commit be7b6a732d
13 changed files with 135 additions and 8 deletions

25
backend/Dockerfile Normal file
View File

@@ -0,0 +1,25 @@
# --- Etapa 1: Build ---
# Usamos la imagen del SDK de .NET 8 para compilar
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
# Copiamos el archivo .csproj para restaurar dependencias primero (cache optimization)
COPY Inventario.API/Inventario.API.csproj Inventario.API/
RUN dotnet restore "Inventario.API/Inventario.API.csproj"
# Copiamos el resto del código fuente
COPY . .
# Publicamos la aplicación en modo Release
WORKDIR "/src/Inventario.API"
RUN dotnet publish "Inventario.API.csproj" -c Release -o /app/publish /p:UseAppHost=false
# --- Etapa 2: Final ---
# Usamos la imagen de runtime de ASP.NET, mucho más ligera
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final
WORKDIR /app
COPY --from=build /app/publish .
# El puerto por defecto que exponen los contenedores de .NET es 8080
EXPOSE 8080
ENTRYPOINT ["dotnet", "Inventario.API.dll"]

View File

@@ -7,7 +7,7 @@
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"ConnectionStrings": { "ConnectionStrings": {
"DefaultConnection": "Server=TECNICA3;Database=InventarioDB;User Id=apiequipos;Password=@Apiequipos513@;TrustServerCertificate=True" "DefaultConnection": "Server=db-sqlserver;Database=InventarioDB;User Id=apiequipos;Password=@Apiequipos513@;TrustServerCertificate=True"
}, },
"SshSettings": { "SshSettings": {
"Host": "192.168.10.1", "Host": "192.168.10.1",

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Inventario.API")] [assembly: System.Reflection.AssemblyCompanyAttribute("Inventario.API")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+99d98cc588b3922b6aa3ab9045fcee9cb31de1f3")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+04f1134be432cfc59ba887bba19eb9b563256780")]
[assembly: System.Reflection.AssemblyProductAttribute("Inventario.API")] [assembly: System.Reflection.AssemblyProductAttribute("Inventario.API")]
[assembly: System.Reflection.AssemblyTitleAttribute("Inventario.API")] [assembly: System.Reflection.AssemblyTitleAttribute("Inventario.API")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

52
docker-compose.yml Normal file
View File

@@ -0,0 +1,52 @@
services:
# Servicio del Backend
inventario-api:
build:
context: .
dockerfile: backend/Dockerfile
container_name: inventario-api
restart: unless-stopped
# 'expose' hace que el puerto sea accesible para otros contenedores en la misma red,
# pero NO lo publica en la máquina host. Esto es lo correcto.
expose:
- "8080"
networks:
- inventario-net
- shared-net # Para conectar con la DB
# Servicio del Frontend
inventario-frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: inventario-frontend
restart: unless-stopped
expose:
- "80" # El Nginx del frontend escucha en el puerto 80 interno
networks:
- inventario-net
# Proxy Inverso que expone los servicios al exterior
inventario-proxy:
image: nginx:1.25-alpine
container_name: inventario-proxy
restart: unless-stopped
volumes:
- ./proxy/nginx.conf:/etc/nginx/conf.d/default.conf
ports:
# Este es el ÚNICO punto de entrada a la aplicación desde el exterior.
- "8900:80"
networks:
- inventario-net
depends_on:
- inventario-api
- inventario-frontend
networks:
# Red interna para la comunicación entre los servicios de este stack
inventario-net:
driver: bridge
# Red externa para conectar con servicios compartidos (la base de datos)
shared-net:
external: true

19
frontend/Dockerfile Normal file
View File

@@ -0,0 +1,19 @@
# --- Etapa 1: Build ---
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# --- Etapa 2: Producción ---
FROM nginx:1.25-alpine
# Copia los archivos estáticos construidos a la carpeta que Nginx sirve por defecto
COPY --from=build /app/dist /usr/share/nginx/html
# Copia la configuración de Nginx específica para el frontend
COPY frontend.nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -0,0 +1,10 @@
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
# Esta línea es crucial para que las rutas de React (SPA) funcionen
try_files $uri $uri/ /index.html;
}
}

21
frontend/proxy/nginx.conf Normal file
View File

@@ -0,0 +1,21 @@
server {
listen 80;
# Pasa todas las peticiones a la API al servicio del backend
location /api/ {
proxy_pass http://inventario-api:8080/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;
}
# Pasa el resto de las peticiones al servicio del frontend
location / {
proxy_pass http://inventario-frontend:80;
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;
}
}

View File

@@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import styles from './SimpleTable.module.css'; import styles from './SimpleTable.module.css';
const BASE_URL = 'http://localhost:5198/api'; const BASE_URL = '/api';
// Interfaces para los diferentes tipos de datos // Interfaces para los diferentes tipos de datos
interface TextValue { interface TextValue {

View File

@@ -4,7 +4,7 @@ import type { Sector } from '../types/interfaces';
import styles from './SimpleTable.module.css'; import styles from './SimpleTable.module.css';
import ModalSector from './ModalSector'; import ModalSector from './ModalSector';
const BASE_URL = 'http://localhost:5198/api'; const BASE_URL = '/api';
const GestionSectores = () => { const GestionSectores = () => {
const [sectores, setSectores] = useState<Sector[]>([]); const [sectores, setSectores] = useState<Sector[]>([]);

View File

@@ -10,7 +10,7 @@ interface ModalAnadirEquipoProps {
onSave: (nuevoEquipo: Omit<Equipo, 'id' | 'created_at' | 'updated_at'>) => void; onSave: (nuevoEquipo: Omit<Equipo, 'id' | 'created_at' | 'updated_at'>) => void;
} }
const BASE_URL = 'http://localhost:5198/api'; const BASE_URL = '/api';
const ModalAnadirEquipo: React.FC<ModalAnadirEquipoProps> = ({ sectores, onClose, onSave }) => { const ModalAnadirEquipo: React.FC<ModalAnadirEquipoProps> = ({ sectores, onClose, onSave }) => {
const [nuevoEquipo, setNuevoEquipo] = useState({ const [nuevoEquipo, setNuevoEquipo] = useState({

View File

@@ -7,7 +7,7 @@ interface Props {
onSave: (usuario: { username: string }) => void; onSave: (usuario: { username: string }) => void;
} }
const BASE_URL = 'http://localhost:5198/api'; const BASE_URL = '/api';
const ModalAnadirUsuario: React.FC<Props> = ({ onClose, onSave }) => { const ModalAnadirUsuario: React.FC<Props> = ({ onClose, onSave }) => {
const [username, setUsername] = useState(''); const [username, setUsername] = useState('');

View File

@@ -19,7 +19,7 @@ interface ModalDetallesEquipoProps {
onAddComponent: (type: 'disco' | 'ram' | 'usuario') => void; onAddComponent: (type: 'disco' | 'ram' | 'usuario') => void;
} }
const BASE_URL = 'http://localhost:5198/api'; const BASE_URL = '/api';
const ModalDetallesEquipo: React.FC<ModalDetallesEquipoProps> = ({ const ModalDetallesEquipo: React.FC<ModalDetallesEquipoProps> = ({
equipo, isOnline, historial, sectores, onClose, onDelete, onRemoveAssociation, onEdit, onAddComponent equipo, isOnline, historial, sectores, onClose, onDelete, onRemoveAssociation, onEdit, onAddComponent

View File

@@ -37,7 +37,7 @@ const SimpleTable = () => {
const [isAddModalOpen, setIsAddModalOpen] = useState(false); const [isAddModalOpen, setIsAddModalOpen] = useState(false);
const [addingComponent, setAddingComponent] = useState<'disco' | 'ram' | 'usuario' | null>(null); const [addingComponent, setAddingComponent] = useState<'disco' | 'ram' | 'usuario' | null>(null);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const BASE_URL = 'http://localhost:5198/api'; const BASE_URL = '/api';
useEffect(() => { useEffect(() => {
const scrollBarWidth = window.innerWidth - document.documentElement.clientWidth; const scrollBarWidth = window.innerWidth - document.documentElement.clientWidth;