Compare commits
	
		
			57 Commits
		
	
	
		
			Suscripcio
			...
			8101756638
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 8101756638 | |||
| b66d00c92d | |||
| 72c2f7ee31 | |||
| 1456ccd723 | |||
| 229f657685 | |||
| 13ab496727 | |||
| 5adc1e6d46 | |||
| 1ec21741cc | |||
| b33dd4f94f | |||
| ba9bef2364 | |||
| 856d7ac5c1 | |||
| d8dc41222c | |||
| a434640456 | |||
| fcc2b90f15 | |||
| 0d7614ef4c | |||
| ae54ef3fe5 | |||
| c361842a91 | |||
| de079d8bd4 | |||
| 401e61e0eb | |||
| 4b208793ce | |||
| d60dd8dc9f | |||
| 8156df9f90 | |||
| 55ca8bffa7 | |||
| 02265a46e7 | |||
| 767cd081dc | |||
| 97b6a9241f | |||
| b68ac1fed1 | |||
| 2e83c2e373 | |||
| 1e8d5fd308 | |||
| a1dfd0d089 | |||
| eff65921e6 | |||
| b3de8dba3a | |||
| f62fc4b507 | |||
| 66686fc548 | |||
| c0900e07e6 | |||
| a63b40471b | |||
| 12decefc1b | |||
| bb79ccf64c | |||
| b3e70a3988 | |||
| 55640c394f | |||
| 338cd8579f | |||
| 69620c5607 | |||
| 8ba18ed687 | |||
| 30570beaca | |||
| db10fa0254 | |||
| 1d664672b2 | |||
| 969c78a567 | |||
| 0b884197fb | |||
| 0d2c30ad94 | |||
| f1591bd572 | |||
| 24e4769f78 | |||
| 76d48cc310 | |||
| b072e31385 | |||
| 65be2bcbaa | |||
| 0fb3cb7aef | |||
| 541625bf66 | |||
| cbe313b59d | 
							
								
								
									
										82
									
								
								.drone.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								.drone.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | |||||||
|  | kind: pipeline | ||||||
|  | type: docker | ||||||
|  | name: Build y Deploy | ||||||
|  |  | ||||||
|  | trigger: | ||||||
|  |   branch: | ||||||
|  |   - main | ||||||
|  |   event: | ||||||
|  |   - push | ||||||
|  |  | ||||||
|  | steps: | ||||||
|  | - name: build-and-publish-backend | ||||||
|  |   image: plugins/kaniko | ||||||
|  |   settings: | ||||||
|  |     repo: host.docker.internal:5000/${DRONE_REPO_OWNER}/${DRONE_REPO_NAME,,}-backend | ||||||
|  |     tags: | ||||||
|  |       - latest | ||||||
|  |       - ${DRONE_COMMIT_SHA:0:8} | ||||||
|  |     dockerfile: Backend/GestionIntegral.Api/Dockerfile | ||||||
|  |     context: . | ||||||
|  |     username: | ||||||
|  |       from_secret: GITEA_USER | ||||||
|  |     password: | ||||||
|  |       from_secret: ACTIONS_PAT | ||||||
|  |     insecure: true | ||||||
|  |  | ||||||
|  | - name: build-and-publish-frontend | ||||||
|  |   image: plugins/kaniko | ||||||
|  |   settings: | ||||||
|  |     repo: host.docker.internal:5000/${DRONE_REPO_OWNER}/${DRONE_REPO_NAME,,}-frontend | ||||||
|  |     tags: | ||||||
|  |       - latest | ||||||
|  |       - ${DRONE_COMMIT_SHA:0:8} | ||||||
|  |     dockerfile: Frontend/Dockerfile | ||||||
|  |     context: . | ||||||
|  |     username: | ||||||
|  |       from_secret: GITEA_USER | ||||||
|  |     password: | ||||||
|  |       from_secret: ACTIONS_PAT | ||||||
|  |     insecure: true | ||||||
|  |   depends_on: | ||||||
|  |     - build-and-publish-backend | ||||||
|  |  | ||||||
|  | - name: deploy-to-production | ||||||
|  |   image: alpine:latest | ||||||
|  |   environment: | ||||||
|  |     SSH_KEY: | ||||||
|  |       from_secret: PROD_SERVER_SSH_KEY | ||||||
|  |     PROD_HOST: | ||||||
|  |       from_secret: PROD_SERVER_HOST | ||||||
|  |     PROD_USER: | ||||||
|  |       from_secret: PROD_SERVER_USER | ||||||
|  |     DB_PASSWORD: | ||||||
|  |       from_secret: DB_SA_PASSWORD_SECRET | ||||||
|  |     JWT_KEY: | ||||||
|  |       from_secret: JWT_KEY_SECRET | ||||||
|  |     REGISTRY: | ||||||
|  |       from_secret: REGISTRY_URL | ||||||
|  |     GITEA_USER: | ||||||
|  |       from_secret: GITEA_USER | ||||||
|  |     GITEA_PAT: | ||||||
|  |       from_secret: ACTIONS_PAT | ||||||
|  |   commands: | ||||||
|  |     - apk add --no-cache openssh-client | ||||||
|  |     - mkdir -p ~/.ssh | ||||||
|  |     - echo "$SSH_KEY" > ~/.ssh/id_rsa | ||||||
|  |     - chmod 600 ~/.ssh/id_rsa | ||||||
|  |     - ssh-keyscan -H $PROD_HOST >> ~/.ssh/known_hosts | ||||||
|  |     - | | ||||||
|  |       ssh $PROD_USER@$PROD_HOST << 'EOF' | ||||||
|  |         echo "--- CONECTADO AL SERVIDOR DE PRODUCCIÓN ---" | ||||||
|  |         cd /opt/gestion-integral | ||||||
|  |         export DB_SA_PASSWORD="${DB_PASSWORD}" | ||||||
|  |         export JWT_KEY="${JWT_KEY}" | ||||||
|  |         docker login ${REGISTRY} -u ${GITEA_USER} -p ${GITEA_PAT} | ||||||
|  |         docker compose pull | ||||||
|  |         docker compose up -d | ||||||
|  |         docker image prune -af | ||||||
|  |         echo "--- DESPLIEGUE COMPLETADO ---" | ||||||
|  |       EOF | ||||||
|  |   depends_on: | ||||||
|  |     - build-and-publish-frontend | ||||||
| @@ -26,7 +26,7 @@ jobs: | |||||||
|             set -e |             set -e | ||||||
|             echo "--- INICIO DEL DESPLIEGUE OPTIMIZADO ---" |             echo "--- INICIO DEL DESPLIEGUE OPTIMIZADO ---" | ||||||
|              |              | ||||||
|             # 1. Preparar entorno |             # 1. Preparar entorno (sin cambios) | ||||||
|             TEMP_DIR=$(mktemp -d) |             TEMP_DIR=$(mktemp -d) | ||||||
|             REPO_OWNER="dmolinari" |             REPO_OWNER="dmolinari" | ||||||
|             REPO_NAME="gestionintegralweb" |             REPO_NAME="gestionintegralweb" | ||||||
| @@ -37,7 +37,7 @@ jobs: | |||||||
|             cd "$TEMP_DIR" |             cd "$TEMP_DIR" | ||||||
|             git checkout "${{ gitea.sha }}" |             git checkout "${{ gitea.sha }}" | ||||||
|              |              | ||||||
|             # 2. Construcción paralela con Docker nativo (más rápido y fiable) |             # 2. Construcción paralela (sin cambios) | ||||||
|             build_image() { |             build_image() { | ||||||
|               local dockerfile=$1 |               local dockerfile=$1 | ||||||
|               local image_name=$2 |               local image_name=$2 | ||||||
| @@ -55,18 +55,31 @@ jobs: | |||||||
|             (build_image "Frontend/Dockerfile" "dmolinari/gestionintegralweb-frontend:latest" ".") & |             (build_image "Frontend/Dockerfile" "dmolinari/gestionintegralweb-frontend:latest" ".") & | ||||||
|             wait |             wait | ||||||
|              |              | ||||||
|             # 3. Despliegue con Docker Compose |             # Copiamos la versión actualizada del docker-compose.yml al directorio de despliegue. | ||||||
|             cd /opt/gestion-integral |             echo "Copiando el archivo docker-compose.yml actualizado..." | ||||||
|             export DB_SA_PASSWORD='${{ secrets.DB_SA_PASSWORD_SECRET }}' |             cp "$TEMP_DIR/docker-compose.yml" /opt/gestion-integral/docker-compose.yml | ||||||
|              |              | ||||||
|             echo "Recreando servicios..." |             # (Opcional pero recomendado) Verificamos que el archivo se copió bien | ||||||
|             docker compose up -d --force-recreate |             echo "--- Verificando contenido del docker-compose.yml que se usará ---" | ||||||
|  |             cat /opt/gestion-integral/docker-compose.yml | head -n 5 | ||||||
|  |             echo "------------------------------------------------------------------" | ||||||
|              |              | ||||||
|             # 4. Limpieza |             # 3. Crear/Actualizar los Docker Secrets (sin cambios) | ||||||
|  |             # ... (tus comandos docker secret create) ... | ||||||
|  |             printf "%s" '${{ secrets.JWT_KEY }}' | docker secret create jwt_key - 2>/dev/null || (printf "%s" '${{ secrets.JWT_KEY }}' | docker secret rm jwt_key && printf "%s" '${{ secrets.JWT_KEY }}' | docker secret create jwt_key -) | ||||||
|  |             printf "%s" '${{ secrets.DB_SA_PASSWORD_SECRET }}' | docker secret create db_password - 2>/dev/null || (printf "%s" '${{ secrets.DB_SA_PASSWORD_SECRET }}' | docker secret rm db_password && printf "%s" '${{ secrets.DB_SA_PASSWORD_SECRET }}' | docker secret create db_password -) | ||||||
|  |              | ||||||
|  |             # 4. Desplegar el Stack | ||||||
|  |             echo "Desplegando el stack..." | ||||||
|  |             docker stack deploy \ | ||||||
|  |               -c /opt/gestion-integral/docker-compose.yml \ | ||||||
|  |               --with-registry-auth \ | ||||||
|  |               gestion-integral | ||||||
|  |              | ||||||
|  |             # 5. Limpieza (sin cambios) | ||||||
|             echo "Realizando limpieza..." |             echo "Realizando limpieza..." | ||||||
|             rm -rf "$TEMP_DIR" |             rm -rf "$TEMP_DIR" | ||||||
|             docker image prune -af --filter "until=24h" |             docker image prune -af --filter "until=24h" | ||||||
|              |              | ||||||
|             echo "--- DESPLIEGUE COMPLETADO CON ÉXITO ---" |             echo "--- DESPLIEGUE COMPLETADO CON ÉXITO ---" | ||||||
|             echo "Tiempo total: $(($SECONDS / 60)) minutos y $(($SECONDS % 60)) segundos" |  | ||||||
|           EOSSH |           EOSSH | ||||||
| @@ -1,29 +1,37 @@ | |||||||
| # --- Etapa 1: Build --- | # --- Etapa 1: Build --- | ||||||
|  | # Usamos el SDK de .NET 9 (o el que corresponda) para compilar. | ||||||
| FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build | FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build | ||||||
| WORKDIR /src | WORKDIR /src | ||||||
|  |  | ||||||
| # Copia solo los archivos de proyecto primero para cachear dependencias | # Copiamos los archivos de proyecto (.sln y .csproj) y restauramos las dependencias. | ||||||
|  | # Esto es una optimización de caché de Docker. | ||||||
| COPY GestionIntegralWeb.sln . | COPY GestionIntegralWeb.sln . | ||||||
| COPY Backend/GestionIntegral.Api/GestionIntegral.Api.csproj Backend/GestionIntegral.Api/ | COPY Backend/GestionIntegral.Api/GestionIntegral.Api.csproj Backend/GestionIntegral.Api/ | ||||||
|  |  | ||||||
| # Restaura dependencias | # Restauramos los paquetes NuGet. | ||||||
| RUN dotnet restore "GestionIntegralWeb.sln" | RUN dotnet restore "GestionIntegralWeb.sln" | ||||||
|  |  | ||||||
| # Copia el resto del código | # Copiamos todo el resto del código fuente. | ||||||
| COPY . . | COPY . . | ||||||
|  |  | ||||||
| # Construye el proyecto | # Nos movemos al directorio del proyecto y lo construimos en modo Release. | ||||||
| WORKDIR "/src/Backend/GestionIntegral.Api" | WORKDIR "/src/Backend/GestionIntegral.Api" | ||||||
| RUN dotnet build "GestionIntegral.Api.csproj" -c Release -o /app/build | RUN dotnet build "GestionIntegral.Api.csproj" -c Release -o /app/build | ||||||
|  |  | ||||||
| # --- Etapa 2: Publish --- | # --- Etapa 2: Publish --- | ||||||
|  | # Publicamos la aplicación, lo que genera los artefactos listos para producción. | ||||||
| FROM build AS publish | FROM build AS publish | ||||||
| RUN dotnet publish "GestionIntegral.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false | RUN dotnet publish "GestionIntegral.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false | ||||||
|  |  | ||||||
| # --- Etapa 3: Final --- | # --- Etapa 3: Final --- | ||||||
|  | # Usamos la imagen de runtime de ASP.NET, que es mucho más ligera que el SDK. | ||||||
| FROM mcr.microsoft.com/dotnet/aspnet:9.0 | FROM mcr.microsoft.com/dotnet/aspnet:9.0 | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
| COPY --from=publish /app/publish . | COPY --from=publish /app/publish . | ||||||
|  |  | ||||||
|  | # El puerto en el que la API escuchará DENTRO del contenedor. | ||||||
|  | # Usaremos 8080 para evitar conflictos si en el futuro corres algo en el puerto 80. | ||||||
| EXPOSE 8080 | EXPOSE 8080 | ||||||
|  |  | ||||||
|  | # El comando para iniciar la API cuando el contenedor arranque. | ||||||
| ENTRYPOINT ["dotnet", "GestionIntegral.Api.dll"] | ENTRYPOINT ["dotnet", "GestionIntegral.Api.dll"] | ||||||
| @@ -112,9 +112,18 @@ builder.Services.AddHealthChecks() | |||||||
| // --- Configuración de Autenticación JWT --- | // --- Configuración de Autenticación JWT --- | ||||||
| var jwtSettings = builder.Configuration.GetSection("Jwt"); | var jwtSettings = builder.Configuration.GetSection("Jwt"); | ||||||
|  |  | ||||||
|  | // Lee la RUTA al archivo del secreto desde la variable de entorno | ||||||
|  | var jwtKeyFilePath = builder.Configuration["JWT_KEY_FILE"];  | ||||||
|  |  | ||||||
|  | // Si la ruta existe (estamos en Docker Swarm), lee el contenido del archivo. | ||||||
|  | // Si no, vuelve a buscar la variable de entorno (para desarrollo local). | ||||||
|  | var jwtKey = !string.IsNullOrEmpty(jwtKeyFilePath) && File.Exists(jwtKeyFilePath) | ||||||
|  |     ? File.ReadAllText(jwtKeyFilePath).Trim() | ||||||
|  |     : builder.Configuration["JWT_KEY"] ?? throw new ArgumentNullException("JWT Key not configured"); | ||||||
|  |  | ||||||
| // Le decimos que busque la clave JWT en la raíz de la configuración (donde están las variables de entorno). | // Le decimos que busque la clave JWT en la raíz de la configuración (donde están las variables de entorno). | ||||||
| // Si no la encuentra, como respaldo, busca en la sección "Jwt" del appsettings. | // Si no la encuentra, como respaldo, busca en la sección "Jwt" del appsettings. | ||||||
| var jwtKey = builder.Configuration["JWT_KEY"] ?? jwtSettings["Key"] ?? throw new ArgumentNullException("JWT_KEY or Jwt:Key not configured"); | //var jwtKey = builder.Configuration["JWT_KEY"] ?? /*jwtSettings["Key"] ??*/ throw new ArgumentNullException("JWT_KEY or Jwt:Key not configured"); | ||||||
|  |  | ||||||
| var keyBytes = Encoding.ASCII.GetBytes(jwtKey); | var keyBytes = Encoding.ASCII.GetBytes(jwtKey); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   "Jwt": { |   "Jwt": { | ||||||
|     "Key": "badb1a38d221c9e23bcf70958840ca7f5a5dc54f2047dadf7ce45b578b5bc3e2", |     "Key": "", | ||||||
|     "Issuer": "GestionIntegralApi", |     "Issuer": "GestionIntegralApi", | ||||||
|     "Audience": "GestionIntegralClient", |     "Audience": "GestionIntegralClient", | ||||||
|     "DurationInHours": 8 |     "DurationInHours": 8 | ||||||
|   | |||||||
| @@ -1,24 +1,31 @@ | |||||||
| # --- Etapa 1: Build --- | # --- Etapa 1: Build --- | ||||||
|  | # Usamos una imagen de Node.js para construir los archivos estáticos de React. | ||||||
| FROM node:20-alpine AS build | FROM node:20-alpine AS build | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
|  |  | ||||||
| # Copia solo dependencias primero para cachear | # Copiamos los archivos de dependencias y las instalamos. | ||||||
| COPY Frontend/package.json Frontend/package-lock.json ./ | COPY package.json package-lock.json ./ | ||||||
| RUN npm install --frozen-lockfile | RUN npm install | ||||||
|  |  | ||||||
| # Copia el resto del código | # Copiamos el resto del código del frontend. | ||||||
| COPY Frontend/. . | COPY . . | ||||||
|  |  | ||||||
| # Construye la aplicación | # Ejecutamos el script de build de Vite, que genera la carpeta 'dist'. | ||||||
| RUN npm run build | RUN npm run build | ||||||
|  |  | ||||||
| # --- Etapa 2: Serve --- | # --- Etapa 2: Serve --- | ||||||
|  | # Usamos una imagen de Nginx súper ligera para servir los archivos estáticos. | ||||||
| FROM nginx:stable-alpine | FROM nginx:stable-alpine | ||||||
| COPY Frontend/nginx.conf /etc/nginx/conf.d/default.conf |  | ||||||
| WORKDIR /usr/share/nginx/html | WORKDIR /usr/share/nginx/html | ||||||
|  |  | ||||||
|  | # Eliminamos el index.html por defecto de Nginx. | ||||||
| RUN rm -f index.html | RUN rm -f index.html | ||||||
|  |  | ||||||
|  | # Copiamos los archivos construidos desde la etapa anterior a la carpeta que Nginx sirve. | ||||||
| COPY --from=build /app/dist . | COPY --from=build /app/dist . | ||||||
|  |  | ||||||
|  | # Nginx por defecto escucha en el puerto 80, así que lo exponemos. | ||||||
| EXPOSE 80 | EXPOSE 80 | ||||||
|  |  | ||||||
|  | # Comando para iniciar Nginx. Esto asegura que se mantenga corriendo. | ||||||
| CMD ["nginx", "-g", "daemon off;"] | CMD ["nginx", "-g", "daemon off;"] | ||||||
		Reference in New Issue
	
	Block a user