From 95c8e7901b3a4e3613948774d1d9db721bb94fd5 Mon Sep 17 00:00:00 2001 From: Ricardo Carneiro Date: Tue, 5 Aug 2025 21:19:40 -0300 Subject: [PATCH] feat: nem build --- Dockerfile | 55 +++----- appsettings.json | 322 +++++++++++++++++++++++++++++------------------ 2 files changed, 222 insertions(+), 155 deletions(-) diff --git a/Dockerfile b/Dockerfile index 95e249f..79d0378 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,53 +2,38 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src -# Copy csproj and restore dependencies (better caching) -COPY ["QRRapidoApp.csproj", "./"] -RUN dotnet restore "QRRapidoApp.csproj" +# Copy csproj and restore as distinct layers +COPY *.csproj ./ +RUN dotnet restore --runtime linux-arm64 -# Copy source code +# Copy everything else and build COPY . . - -# Build optimized for production -RUN dotnet build "QRRapidoApp.csproj" -c Release -o /app/build --no-restore +RUN dotnet build "QRRapidoApp.csproj" -c Release --runtime linux-arm64 --no-restore # Publish stage FROM build AS publish -RUN dotnet publish "QRRapidoApp.csproj" -c Release -o /app/publish --no-restore --no-build \ - /p:PublishReadyToRun=true \ - /p:PublishSingleFile=false \ - /p:PublishTrimmed=false +RUN dotnet publish "QRRapidoApp.csproj" -c Release -o /app/publish \ + --runtime linux-arm64 \ + --no-restore \ + --no-build \ + --self-contained false -# Runtime stage +# Final stage/image FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final WORKDIR /app -# Install dependencies for QR code generation -RUN apt-get update && apt-get install -y \ - libgdiplus \ - libc6-dev \ - curl \ - && rm -rf /var/lib/apt/lists/* +# Install libgdiplus for System.Drawing (se necessário para QR codes) +RUN apt-get update && apt-get install -y libgdiplus && rm -rf /var/lib/apt/lists/* -# Copy application +# Copy published app COPY --from=publish /app/publish . -# Configure production environment -ENV ASPNETCORE_ENVIRONMENT=Production -ENV ASPNETCORE_URLS=http://+:80 -ENV DOTNET_EnableDiagnostics=0 +# Expose port +EXPOSE 8080 -# Create non-root user for security -RUN addgroup --system --gid 1001 qrrapido -RUN adduser --system --uid 1001 qrrapido +# Create non-root user +RUN adduser --disabled-password --gecos '' appuser && chown -R appuser /app +USER appuser -# Set ownership -RUN chown -R qrrapido:qrrapido /app -USER qrrapido - -# Health check -HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ - CMD curl -f http://localhost/health || exit 1 - -EXPOSE 80 +# Set entry point ENTRYPOINT ["dotnet", "QRRapidoApp.dll"] \ No newline at end of file diff --git a/appsettings.json b/appsettings.json index bfd97d6..f56a990 100644 --- a/appsettings.json +++ b/appsettings.json @@ -1,120 +1,202 @@ -{ - "App": { - "Name": "QR Rapido", - "BaseUrl": "https://qrrapido.site", - "TaglinePT": "Gere QR codes em segundos!", - "TaglineES": "¡Genera códigos QR en segundos!", - "TaglineEN": "Generate QR codes in seconds!", - "Version": "1.0.0" - }, - "ConnectionStrings": { - "MongoDB": "mongodb://localhost:27017/QrRapido" - }, - "Authentication": { - "Google": { - "ClientId": "1080447252222-dqjsu999tvrpb69oj5iapckdh9g8rvha.apps.googleusercontent.com", - "ClientSecret": "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" - }, - "Microsoft": { - "ClientId": "9bec3835-acdb-4c5a-8668-6b90955c6ad2", - "ClientSecret": "Oe38Q~FsZ3X5ouptAB6oYyX7MXaGUvxXcqT.aaT9" - } - }, - "Stripe": { - "PublishableKey": "pk_test_51Rs42tBeR5IFYUsBooapyDwQTgh6CFuKbya5R3MVDTrdOUKmgiHQYipU0pgOdG5iKogH77RUYIKBJzbCt5BghUOY00xitV5KiN", - "SecretKey": "sk_test_51Rs42tBeR5IFYUsBtycRlJJcdwgoMbh8MfQIKIGelBPTQFwDcOn2iCCbw5uG6hnqlpgNAUuFgWRAUUMA8qkABKun00EIx4odDF", - "WebhookSecret": "whsec_2e828803ceb48e7865458b0cf332b68535fdff8753d26d69b1c88ea55cb0e482", - "PriceId": "prod_SnfQTxwE3i8r5L" - }, - "AdSense": { - "ClientId": "ca-pub-XXXXXXXXXX", - "Enabled": true - }, - "Performance": { - "QRGenerationTimeoutMs": 2000, - "CacheExpirationMinutes": 60, - "MaxConcurrentGenerations": 100 - }, - "HistoryCleanup": { - "GracePeriodDays": 7, - "CleanupIntervalHours": 6 - }, - "Premium": { - "FreeQRLimit": 10, - "PremiumPrice": 12.90, - "Features": { - "UnlimitedQR": true, - "DynamicQR": true, - "NoAds": true, - "PrioritySupport": true, - "AdvancedAnalytics": true, - "SpeedBoost": true - } - }, - "SEO": { - "KeywordsPT": "qr rapido, gerador qr rapido, qr code rapido, codigo qr rapido, qr gratis rapido", - "KeywordsES": "qr rapido, generador qr rapido, codigo qr rapido, qr gratis rapido", - "KeywordsEN": "fast qr, quick qr generator, rapid qr code, qr code generator" - }, - "ApplicationName": "QRRapido", - "Environment": "Personal", - "Serilog": { - "SeqUrl": "http://localhost:5341", - "ApiKey": "", - "MinimumLevel": { - "Default": "Information", - "Override": { - "Microsoft": "Warning", - "Microsoft.AspNetCore": "Warning", - "Microsoft.Hosting.Lifetime": "Information", - "System": "Warning" - } - } - }, - "ResourceMonitoring": { - "Enabled": true, - "IntervalSeconds": 30, - "CpuThresholdPercent": 80, - "MemoryThresholdMB": 512, - "ConsecutiveAlertsBeforeError": 4, - "GcCollectionThreshold": 10 - }, - "MongoDbMonitoring": { - "Enabled": true, - "IntervalMinutes": 5, - "DatabaseSizeWarningMB": 1024, - "DatabaseSizeErrorMB": 5120, - "GrowthRateWarningMBPerHour": 100, - "IncludeCollectionStats": true, - "CollectionsToMonitor": [ "Users", "QRCodeHistory", "AdFreeSessions" ] - }, - "HealthChecks": { - "MongoDB": { - "TimeoutSeconds": 5, - "IncludeDatabaseSize": true, - "TestQuery": true - }, - "Seq": { - "TimeoutSeconds": 3, - "TestLogMessage": "QRRapido health check test" - }, - "Resources": { - "CpuThresholdPercent": 85, - "MemoryThresholdMB": 600, - "GcPressureThreshold": 15 - }, - "ExternalServices": { - "TimeoutSeconds": 10, - "TestStripeConnection": true, - "TestGoogleAuth": false, - "TestMicrosoftAuth": false - } - }, - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" -} \ No newline at end of file +name: Deploy QR Rapido +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main ] + +env: + REGISTRY: registry.redecarneir.us + IMAGE_NAME: qrrapido + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore --configuration Release + + - name: Test + run: dotnet test --no-build --configuration Release --verbosity normal --collect:"XPlat Code Coverage" + + - name: Upload coverage + uses: codecov/codecov-action@v3 + with: + files: coverage.cobertura.xml + + build-and-push: + needs: test + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push to registry + run: | + # Determina a tag baseada na branch + if [ "${{ github.ref }}" = "refs/heads/main" ]; then + TAG="latest" + else + TAG="develop" + fi + + # Build da imagem para ARM64 (servidores Ampere OCI) + docker buildx build \ + --platform linux/arm64 \ + --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:$TAG \ + --push \ + . + + echo "IMAGE_TAG=$TAG" >> $GITHUB_ENV + + deploy-staging: + needs: build-and-push + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/develop' + + steps: + - name: Deploy to Staging Servers + run: | + # Configura SSH known_hosts + mkdir -p ~/.ssh + ssh-keyscan -H 141.148.162.114 >> ~/.ssh/known_hosts + ssh-keyscan -H 129.146.116.218 >> ~/.ssh/known_hosts + + # Deploy no Servidor 1 + ssh ubuntu@141.148.162.114 << 'EOF' + # Para o container atual se existir + docker stop qrrapido-staging || true + docker rm qrrapido-staging || true + + # Remove imagem antiga + docker rmi ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:develop || true + + # Puxa nova imagem + docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:develop + + # Executa novo container + docker run -d \ + --name qrrapido-staging \ + --restart unless-stopped \ + -p 5000:8080 \ + -e ASPNETCORE_ENVIRONMENT=Staging \ + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:develop + EOF + + # Deploy no Servidor 2 + ssh ubuntu@129.146.116.218 << 'EOF' + # Para o container atual se existir + docker stop qrrapido-staging || true + docker rm qrrapido-staging || true + + # Remove imagem antiga + docker rmi ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:develop || true + + # Puxa nova imagem + docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:develop + + # Executa novo container + docker run -d \ + --name qrrapido-staging \ + --restart unless-stopped \ + -p 5000:8080 \ + -e ASPNETCORE_ENVIRONMENT=Staging \ + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:develop + EOF + + deploy-production: + needs: build-and-push + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + environment: production + + steps: + - name: Deploy to Production Servers + run: | + # Configura SSH known_hosts + mkdir -p ~/.ssh + ssh-keyscan -H 141.148.162.114 >> ~/.ssh/known_hosts + ssh-keyscan -H 129.146.116.218 >> ~/.ssh/known_hosts + + # Deploy no Servidor 1 (com NGINX) + ssh ubuntu@141.148.162.114 << 'EOF' + # Para o container atual se existir + docker stop qrrapido-prod || true + docker rm qrrapido-prod || true + + # Remove imagem antiga + docker rmi ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest || true + + # Puxa nova imagem + docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + + # Executa novo container + docker run -d \ + --name qrrapido-prod \ + --restart unless-stopped \ + -p 5001:8080 \ + -e ASPNETCORE_ENVIRONMENT=Production \ + -e ASPNETCORE_URLS=http://+:8080 \ + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + + # Recarrega NGINX para garantir que está apontando para o novo container + sudo nginx -t && sudo systemctl reload nginx + EOF + + # Deploy no Servidor 2 + ssh ubuntu@129.146.116.218 << 'EOF' + # Para o container atual se existir + docker stop qrrapido-prod || true + docker rm qrrapido-prod || true + + # Remove imagem antiga + docker rmi ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest || true + + # Puxa nova imagem + docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + + # Executa novo container + docker run -d \ + --name qrrapido-prod \ + --restart unless-stopped \ + -p 5001:8080 \ + -e ASPNETCORE_ENVIRONMENT=Production \ + -e ASPNETCORE_URLS=http://+:8080 \ + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + EOF + + - name: Health Check + run: | + # Aguarda um pouco para os containers subirem + sleep 30 + + # Verifica se os serviços estão respondendo + echo "Verificando Servidor 1..." + ssh ubuntu@141.148.162.114 'curl -f http://localhost:5001/health || echo "Servidor 1 pode não estar respondendo"' + + echo "Verificando Servidor 2..." + ssh ubuntu@129.146.116.218 'curl -f http://localhost:5001/health || echo "Servidor 2 pode não estar respondendo"' \ No newline at end of file