first commit

This commit is contained in:
Ricardo Carneiro 2025-07-22 21:24:07 -03:00
commit dfa9ae0225
48 changed files with 13122 additions and 0 deletions

View File

@ -0,0 +1,9 @@
{
"permissions": {
"allow": [
"Bash(mkdir:*)",
"Bash(chmod:*)"
],
"deny": []
}
}

190
README-NEW-ARCHITECTURE.md Normal file
View File

@ -0,0 +1,190 @@
# BCards Infrastructure - Nova Arquitetura Simplificada
## Mudanças Implementadas
### ❌ **Removido: Docker Swarm**
- Eliminado problemas com caracteres especiais em tokens
- Removida complexidade desnecessária do Swarm
- Não mais dependente de conectividade entre servidores
### ✅ **Nova Arquitetura: Containers Independentes**
#### **Servidor 1** (129.153.123.92)
- **Função**: Load Balancer + App Container
- **Componentes**:
- NGINX (Load Balancer para ambos servidores)
- Container bcards-app-server1 na porta 8080
- SSL/TLS com Let's Encrypt
#### **Servidor 2** (129.146.116.218)
- **Função**: App Container
- **Componentes**:
- Container bcards-app-server2 na porta 8080
- Funciona independentemente
## Scripts Atualizados
### 1. **setup-servers.sh**
```bash
# O que mudou:
- ❌ Removido: setup_docker_swarm()
- ❌ Removido: Portas Swarm no firewall (2377, 7946, 4789)
- ✅ Adicionado: setup_nginx() - Instala NGINX
- ✅ Mudado: Redes Docker agora são locais (bridge)
```
### 2. **deploy-to-servers.sh**
```bash
# O que mudou:
- ❌ Removido: Docker Swarm stack deployment
- ❌ Removido: Swarm service management
- ✅ Adicionado: Docker Compose independente por servidor
- ✅ Mudado: Containers com portas expostas (8080)
- ✅ Melhorado: Health checks individuais por container
```
### 3. **setup-nginx-loadbalancer.sh** (NOVO)
```bash
# Novo script para:
- Configurar NGINX como load balancer
- Setup SSL com Let's Encrypt
- Configurar redirecionamento HTTP->HTTPS
- Testar conectividade com backends
```
## Fluxo de Deploy
### 1. **Setup Inicial**
```bash
# Configurar servidores
./scripts/setup-servers.sh
# Configurar NGINX Load Balancer
./scripts/setup-nginx-loadbalancer.sh --ssl
```
### 2. **Deploy da Aplicação**
```bash
# Deploy normal
./scripts/deploy-to-servers.sh
# Deploy versão específica
./scripts/deploy-to-servers.sh v1.2.3
# Deploy sem rebuild
./scripts/deploy-to-servers.sh latest --skip-build
```
## Benefícios da Nova Arquitetura
### ✅ **Simplicidade**
- Sem tokens complexos do Swarm
- Cada servidor funciona independentemente
- Fácil debug e troubleshooting
### ✅ **Robustez**
- Falha em um servidor não afeta o outro
- Rollback independente por servidor
- Load balancer detecta servidores down
### ✅ **Facilidade de Manutenção**
- Docker Compose familiar e simples
- Logs isolados por servidor
- Updates independentes
### ✅ **Flexibilidade**
- Fácil adicionar/remover servidores
- Configuração por servidor personalizada
- Escalabilidade horizontal simples
## Configuração do Load Balancer
### **NGINX Upstream**
```nginx
upstream bcards_backend {
server 129.153.123.92:8080;
server 129.146.116.218:8080;
keepalive 32;
}
```
### **Health Checks**
- NGINX detecta automaticamente servidores indisponíveis
- Failover automático entre servidores
- Health check endpoint: `/health`
### **SSL/TLS**
- Let's Encrypt automático
- Redirecionamento HTTP->HTTPS
- Headers de segurança configurados
## Comandos Úteis
### **Verificar Status**
```bash
# Status dos containers
ssh ubuntu@129.153.123.92 "sudo docker ps"
ssh ubuntu@129.146.116.218 "sudo docker ps"
# Status do NGINX
ssh ubuntu@129.153.123.92 "sudo systemctl status nginx"
# Logs da aplicação
ssh ubuntu@129.153.123.92 "sudo docker logs bcards-app-server1"
ssh ubuntu@129.146.116.218 "sudo docker logs bcards-app-server2"
```
### **Troubleshooting**
```bash
# Teste de conectividade
curl -f http://bcards.site/health
curl -f https://bcards.site/health
# Logs do NGINX
ssh ubuntu@129.153.123.92 "sudo tail -f /var/log/nginx/bcards_access.log"
ssh ubuntu@129.153.123.92 "sudo tail -f /var/log/nginx/bcards_error.log"
# Restart individual
ssh ubuntu@129.153.123.92 "cd ~/bcards && sudo docker compose restart"
ssh ubuntu@129.146.116.218 "cd ~/bcards && sudo docker compose restart"
```
## Migração da Arquitetura Anterior
### **Para migrar do Docker Swarm:**
1. **Backup** (se necessário):
```bash
# Backup de dados importantes
ssh ubuntu@129.153.123.92 "sudo docker cp container_name:/data ./backup/"
```
2. **Limpar Swarm**:
```bash
ssh ubuntu@129.153.123.92 "sudo docker swarm leave --force"
ssh ubuntu@129.146.116.218 "sudo docker swarm leave --force"
```
3. **Deploy nova arquitetura**:
```bash
./scripts/setup-servers.sh
./scripts/setup-nginx-loadbalancer.sh --ssl
./scripts/deploy-to-servers.sh
```
## Monitoramento
### **Health Checks**
- **Endpoint**: `https://bcards.site/health`
- **Frequência**: 30s por container
- **Timeout**: 10s
- **Retries**: 3
### **Logs**
- **NGINX**: `/var/log/nginx/bcards_*.log`
- **Containers**: Docker logs com rotação automática
- **Sistema**: `/var/log/bcards/`
---
**✅ Arquitetura simplificada e robusta implementada com sucesso!**

293
README.md Normal file
View File

@ -0,0 +1,293 @@
# BCards Infrastructure
Infraestrutura completa para deploy do BCards com load balancer, SSL e zero-downtime deployment.
## 📋 Visão Geral
### Arquitetura
- **2 Servidores OCI**: 129.153.123.92 e 129.146.116.218
- **Load Balancer**: NGINX com SSL (Let's Encrypt)
- **Orquestração**: Docker Swarm
- **Domínio**: bcards.site
- **Registry**: registry.redecarneir.us
### Componentes
- **Aplicação ASP.NET Core**: App de teste com identificação de servidor
- **NGINX**: Load balancer com SSL e health checks
- **Docker Swarm**: Orquestração de containers
- **Fluentd**: Agregação de logs
- **Monitoramento**: Prometheus + Grafana (opcional)
## 🚀 Início Rápido
### 1. Setup dos Servidores
```bash
# Configurar servidores OCI
./scripts/setup-servers.sh
```
### 2. Configurar SSL
```bash
# Configurar certificados SSL
cd nginx && ./setup-ssl.sh
```
### 3. Deploy da Aplicação
```bash
# Deploy para produção
./scripts/deploy-to-servers.sh
```
### 4. Teste Local
```bash
# Testar localmente
docker-compose -f docker-compose.test.yml up
```
## 📁 Estrutura do Projeto
```
bcards-infrastructure/
├── scripts/
│ ├── setup-servers.sh # Setup inicial dos servidores
│ ├── deploy-to-servers.sh # Deploy automatizado
│ ├── test-connectivity.sh # Testes de conectividade
│ └── cleanup.sh # Limpeza de recursos
├── test-app/ # Aplicação ASP.NET Core de teste
│ ├── Controllers/
│ ├── Views/
│ ├── Program.cs
│ ├── Dockerfile
│ └── docker-compose.yml
├── nginx/ # Configurações NGINX
│ ├── nginx.conf # Configuração produção
│ ├── nginx-test.conf # Configuração teste
│ ├── setup-ssl.sh # Script SSL
│ └── fluentd/ # Configurações logs
├── monitoring/ # Monitoramento
│ └── prometheus.yml
├── mongodb/ # Configurações MongoDB
│ └── init/
└── docker-compose.test.yml # Ambiente de teste
```
## 🛠️ Scripts Disponíveis
### Setup dos Servidores
```bash
./scripts/setup-servers.sh
```
- Instala Docker se necessário
- Configura Docker Swarm
- Configura firewall básico
- Cria estrutura de diretórios
### Deploy Automatizado
```bash
# Deploy versão latest
./scripts/deploy-to-servers.sh
# Deploy versão específica
./scripts/deploy-to-servers.sh v1.2.3
# Deploy sem rebuild
./scripts/deploy-to-servers.sh latest --skip-build
# Deploy com rollback automático
./scripts/deploy-to-servers.sh latest --auto-rollback
```
### Testes de Conectividade
```bash
# Teste completo
./scripts/test-connectivity.sh
# Gerar relatório
./scripts/test-connectivity.sh --report
```
### Limpeza de Recursos
```bash
# Limpeza básica
./scripts/cleanup.sh
# Parar serviços e limpar
./scripts/cleanup.sh --stop-services
# Limpeza completa (cuidado!)
./scripts/cleanup.sh --full
```
## 🌐 Endpoints
### Produção (bcards.site)
- **Aplicação**: https://bcards.site
- **Health Check**: https://bcards.site/health
- **Server Info**: https://bcards.site/server-info
- **NGINX Status**: https://bcards.site/nginx-health
### Teste Local (localhost)
- **Aplicação**: http://localhost
- **NGINX Status**: http://localhost:8081/nginx_status
- **Prometheus**: http://localhost:9090
- **Grafana**: http://localhost:3000 (admin/bcards123)
## 🔧 Configuração
### Variáveis de Ambiente
#### Aplicação
- `SERVER_NAME`: Nome do servidor (ex: "BCards Server 1")
- `SERVER_COLOR`: Cor do tema (ex: "#28a745")
- `ASPNETCORE_ENVIRONMENT`: Ambiente (Production/Development)
#### Docker Swarm
- Labels dos nodes: `server_id=server1` ou `server_id=server2`
### Portas Utilizadas
#### Produção
- **80**: HTTP (redirect para HTTPS)
- **443**: HTTPS
- **2377**: Docker Swarm management
- **7946**: Docker Swarm communication
- **4789**: Docker overlay network
#### Teste Local
- **80**: NGINX Load Balancer
- **3000**: Grafana
- **6379**: Redis
- **9090**: Prometheus
- **24224**: Fluentd
- **27017**: MongoDB
## 📊 Monitoramento
### Logs
- **NGINX**: `/var/log/nginx/`
- **Aplicação**: Docker logs via Fluentd
- **Sistema**: journalctl
### Health Checks
- **Aplicação**: `/health` endpoint
- **NGINX**: `/nginx-health` endpoint
- **Docker**: Container health checks
### Métricas
- **Prometheus**: Coleta de métricas
- **Grafana**: Visualização de dashboards
## 🔒 Segurança
### SSL/TLS
- **Let's Encrypt**: Certificados automáticos
- **Renovação**: Automática via cron
- **HSTS**: Habilitado
- **Strong Ciphers**: TLS 1.2+
### Firewall
- **SSH**: Porta 22
- **HTTP/HTTPS**: Portas 80/443
- **Docker Swarm**: Portas necessárias
- **Resto**: Bloqueado
### Docker
- **Usuário não-root**: Containers executam como usuário limitado
- **Resources limits**: Limites de CPU/memória
- **Health checks**: Monitoramento de saúde
## 🚨 Troubleshooting
### Problemas Comuns
#### SSH não conecta
```bash
# Verificar conectividade
ssh -o ConnectTimeout=10 ubuntu@129.153.123.92 "echo 'ok'"
```
#### Docker Swarm não funciona
```bash
# Verificar status do swarm
ssh ubuntu@129.153.123.92 "sudo docker node ls"
# Reinicializar se necessário
./scripts/setup-servers.sh
```
#### SSL não funciona
```bash
# Verificar certificados
sudo openssl x509 -in /etc/letsencrypt/live/bcards.site/fullchain.pem -text -noout
# Renovar certificados
sudo /usr/local/bin/renew-ssl.sh
```
#### Load balancer não balanceia
```bash
# Testar múltiplas requisições
for i in {1..10}; do curl -s http://bcards.site/server-info | jq -r '.ServerName'; done
```
### Logs Úteis
```bash
# Logs do NGINX
sudo docker service logs bcards-nginx
# Logs da aplicação
sudo docker service logs bcards-app_app-server1
sudo docker service logs bcards-app_app-server2
# Logs do sistema
sudo journalctl -u docker -f
```
## 📚 Recursos Adicionais
### Comandos Docker Swarm
```bash
# Listar serviços
sudo docker service ls
# Escalar serviço
sudo docker service scale bcards-app_app-server1=2
# Atualizar serviço
sudo docker service update --image registry.redecarneir.us/bcards-test-app:v2 bcards-app_app-server1
# Rollback
sudo docker service rollback bcards-app_app-server1
```
### Backup e Restore
```bash
# Backup da configuração
tar -czf bcards-backup-$(date +%Y%m%d).tar.gz scripts/ nginx/ test-app/
# Backup do banco (se usando MongoDB)
docker exec bcards-mongodb mongodump --out /backup
```
## 🤝 Contribuição
1. Fork o projeto
2. Crie uma branch para sua feature (`git checkout -b feature/AmazingFeature`)
3. Commit suas mudanças (`git commit -m 'Add some AmazingFeature'`)
4. Push para a branch (`git push origin feature/AmazingFeature`)
5. Abra um Pull Request
## 📄 Licença
Este projeto está sob a licença MIT. Veja o arquivo `LICENSE` para mais detalhes.
## 📞 Suporte
Para suporte, entre em contato:
- **Email**: admin@bcards.site
- **Issues**: GitHub Issues
- **Documentação**: README.md
---
**BCards Infrastructure** - Deploy com confiança! 🚀

24
bcards-infrastructure.sln Normal file
View File

@ -0,0 +1,24 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BCardsTestApp", "test-app\BCardsTestApp.csproj", "{D3618677-0EF3-8C7D-E6B2-197D93585C5E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D3618677-0EF3-8C7D-E6B2-197D93585C5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D3618677-0EF3-8C7D-E6B2-197D93585C5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D3618677-0EF3-8C7D-E6B2-197D93585C5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D3618677-0EF3-8C7D-E6B2-197D93585C5E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {43F40495-4B27-4C15-A2BE-023117CD44C5}
EndGlobalSection
EndGlobal

187
docker-compose.test.yml Normal file
View File

@ -0,0 +1,187 @@
version: '3.8'
# Docker Compose para teste local da infraestrutura BCards
# Simula o ambiente de produção com 2 servidores e load balancer
services:
# Aplicação Server 1
bcards-server1:
build: ./test-app
container_name: bcards-server1
environment:
- ASPNETCORE_ENVIRONMENT=Development
- SERVER_NAME=BCards Server 1
- SERVER_COLOR=#28a745
- ASPNETCORE_URLS=http://+:8080
networks:
- bcards-test-network
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
depends_on:
- mongodb
# Aplicação Server 2
bcards-server2:
build: ./test-app
container_name: bcards-server2
environment:
- ASPNETCORE_ENVIRONMENT=Development
- SERVER_NAME=BCards Server 2
- SERVER_COLOR=#007bff
- ASPNETCORE_URLS=http://+:8080
networks:
- bcards-test-network
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
depends_on:
- mongodb
# NGINX Load Balancer
nginx-lb:
image: nginx:alpine
container_name: bcards-nginx-lb
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx-test.conf:/etc/nginx/nginx.conf:ro
- ./nginx/logs:/var/log/nginx
networks:
- bcards-test-network
restart: unless-stopped
depends_on:
- bcards-server1
- bcards-server2
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/nginx-health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
# MongoDB para testes
mongodb:
image: mongo:7.0
container_name: bcards-mongodb
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=bcards123
- MONGO_INITDB_DATABASE=bcards
ports:
- "27017:27017"
volumes:
- mongodb_data:/data/db
- ./mongodb/init:/docker-entrypoint-initdb.d:ro
networks:
- bcards-test-network
restart: unless-stopped
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
# Redis para cache (opcional)
redis:
image: redis:7-alpine
container_name: bcards-redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
networks:
- bcards-test-network
restart: unless-stopped
command: redis-server /usr/local/etc/redis/redis.conf
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
# Fluentd para logs centralizados
fluentd:
image: fluent/fluentd:v1.16-debian-1
container_name: bcards-fluentd
volumes:
- ./nginx/fluentd/fluent-test.conf:/fluentd/etc/fluent.conf:ro
- ./nginx/logs:/var/log/nginx:ro
- fluentd_logs:/fluentd/log
ports:
- "24224:24224"
- "24224:24224/udp"
networks:
- bcards-test-network
restart: unless-stopped
environment:
- FLUENTD_CONF=fluent.conf
# Prometheus para monitoramento (opcional)
prometheus:
image: prom/prometheus:latest
container_name: bcards-prometheus
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
networks:
- bcards-test-network
restart: unless-stopped
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--storage.tsdb.retention.time=200h'
- '--web.enable-lifecycle'
# Grafana para dashboards (opcional)
grafana:
image: grafana/grafana:latest
container_name: bcards-grafana
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
- ./monitoring/grafana/provisioning:/etc/grafana/provisioning:ro
networks:
- bcards-test-network
restart: unless-stopped
environment:
- GF_SECURITY_ADMIN_PASSWORD=bcards123
- GF_USERS_ALLOW_SIGN_UP=false
depends_on:
- prometheus
networks:
bcards-test-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
volumes:
mongodb_data:
driver: local
redis_data:
driver: local
fluentd_logs:
driver: local
prometheus_data:
driver: local
grafana_data:
driver: local

View File

@ -0,0 +1,52 @@
// MongoDB initialization script for BCards
// Create BCards database
db = db.getSiblingDB('bcards');
// Create collections
db.createCollection('users');
db.createCollection('cards');
db.createCollection('sessions');
// Create indexes
db.users.createIndex({ "email": 1 }, { unique: true });
db.cards.createIndex({ "userId": 1 });
db.cards.createIndex({ "createdAt": 1 });
db.sessions.createIndex({ "createdAt": 1 }, { expireAfterSeconds: 3600 });
// Insert sample data for testing
db.users.insertMany([
{
_id: ObjectId(),
email: "test1@bcards.site",
name: "Test User 1",
createdAt: new Date()
},
{
_id: ObjectId(),
email: "test2@bcards.site",
name: "Test User 2",
createdAt: new Date()
}
]);
db.cards.insertMany([
{
_id: ObjectId(),
userId: db.users.findOne({"email": "test1@bcards.site"})._id,
title: "Test Card 1",
content: "This is a test card for infrastructure testing",
tags: ["test", "infrastructure"],
createdAt: new Date()
},
{
_id: ObjectId(),
userId: db.users.findOne({"email": "test2@bcards.site"})._id,
title: "Test Card 2",
content: "Another test card for load balancer testing",
tags: ["test", "load-balancer"],
createdAt: new Date()
}
]);
print("BCards database initialized successfully!");

39
monitoring/prometheus.yml Normal file
View File

@ -0,0 +1,39 @@
# Prometheus configuration for BCards monitoring
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
scrape_configs:
# Prometheus itself
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# NGINX metrics (via nginx-prometheus-exporter if installed)
- job_name: 'nginx'
static_configs:
- targets: ['nginx-lb:8081']
metrics_path: /nginx_status
scrape_interval: 30s
# Application health checks
- job_name: 'bcards-app'
static_configs:
- targets: ['bcards-server1:8080', 'bcards-server2:8080']
metrics_path: /health
scrape_interval: 30s
# MongoDB exporter (if installed)
# - job_name: 'mongodb'
# static_configs:
# - targets: ['mongodb-exporter:9216']
# Redis exporter (if installed)
# - job_name: 'redis'
# static_configs:
# - targets: ['redis-exporter:9121']

64
nginx/docker-compose.yml Normal file
View File

@ -0,0 +1,64 @@
version: '3.8'
services:
# NGINX Load Balancer
nginx:
image: nginx:alpine
container_name: bcards-nginx
ports:
- "80:80"
- "443:443"
volumes:
# Configuração do NGINX
- ./nginx.conf:/etc/nginx/nginx.conf:ro
# Certificados SSL (Let's Encrypt)
- /etc/letsencrypt:/etc/letsencrypt:ro
- /var/www/certbot:/var/www/certbot:ro
# Logs
- ./logs:/var/log/nginx
networks:
- bcards-network
restart: unless-stopped
depends_on:
- certbot
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/nginx-health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
labels:
- "traefik.enable=false"
# Certbot para SSL certificates
certbot:
image: certbot/certbot
container_name: bcards-certbot
volumes:
- /etc/letsencrypt:/etc/letsencrypt
- /var/www/certbot:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
restart: unless-stopped
# Log aggregator (opcional)
fluentd:
image: fluent/fluentd:v1.16-debian-1
container_name: bcards-fluentd
volumes:
- ./fluentd/fluent.conf:/fluentd/etc/fluent.conf:ro
- ./logs:/var/log/nginx:ro
- ./fluentd/logs:/fluentd/log
ports:
- "24224:24224"
- "24224:24224/udp"
networks:
- bcards-network
restart: unless-stopped
environment:
- FLUENTD_CONF=fluent.conf
networks:
bcards-network:
external: true

View File

@ -0,0 +1,44 @@
# Fluentd configuration for BCards test environment
<source>
@type tail
path /var/log/nginx/access.log
pos_file /fluentd/log/access.log.pos
tag nginx.access
format nginx
time_format %d/%b/%Y:%H:%M:%S %z
</source>
<source>
@type tail
path /var/log/nginx/error.log
pos_file /fluentd/log/error.log.pos
tag nginx.error
format /^(?<time>\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}) \[(?<level>\w+)\] (?<message>.*)/
time_format %Y/%m/%d %H:%M:%S
</source>
<filter nginx.**>
@type record_transformer
<record>
hostname "#{Socket.gethostname}"
service nginx
environment test
</record>
</filter>
<match nginx.**>
@type file
path /fluentd/log/nginx-test
append true
time_slice_format %Y%m%d
time_slice_wait 10m
time_format %Y%m%dT%H%M%S%z
buffer_type file
buffer_path /fluentd/log/nginx.buffer
flush_interval 30s
</match>
<match **>
@type stdout
</match>

40
nginx/fluentd/fluent.conf Normal file
View File

@ -0,0 +1,40 @@
# Fluentd configuration for BCards log aggregation
<source>
@type tail
path /var/log/nginx/access.log
pos_file /fluentd/log/access.log.pos
tag nginx.access
format nginx
time_format %d/%b/%Y:%H:%M:%S %z
</source>
<source>
@type tail
path /var/log/nginx/error.log
pos_file /fluentd/log/error.log.pos
tag nginx.error
format /^(?<time>\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}) \[(?<level>\w+)\] (?<message>.*)/
time_format %Y/%m/%d %H:%M:%S
</source>
<filter nginx.**>
@type record_transformer
<record>
hostname "#{Socket.gethostname}"
service nginx
environment production
</record>
</filter>
<match nginx.**>
@type file
path /fluentd/log/nginx
append true
time_slice_format %Y%m%d
time_slice_wait 10m
time_format %Y%m%dT%H%M%S%z
buffer_type file
buffer_path /fluentd/log/nginx.buffer
flush_interval 30s
</match>

View File

@ -0,0 +1,74 @@
upstream bcards_backend {
server 141.148.162.114:8080;
server 129.146.116.218:8080;
# Health check settings
keepalive 32;
}
server {
listen 80;
server_name bcards.site;
# Security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
# Load balancer configuration
location / {
proxy_pass http://bcards_backend;
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;
# Connection settings
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
# Buffer settings
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
# Health check
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 30s;
}
# Health check endpoint
location /health {
access_log off;
proxy_pass http://bcards_backend;
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;
}
# Static files (if any)
location /static {
proxy_pass http://bcards_backend;
proxy_cache_valid 200 1h;
expires 1h;
add_header Cache-Control "public, immutable";
}
# Let's Encrypt ACME challenge
location /.well-known/acme-challenge/ {
root /var/www/html;
}
# Logging
access_log /var/log/nginx/bcards_access.log;
error_log /var/log/nginx/bcards_error.log;
}

View File

@ -0,0 +1,87 @@
upstream bcards_backend {
server 141.148.162.114:8080;
server 129.146.116.218:8080;
# Health check settings
keepalive 32;
}
server {
listen 80;
server_name bcards.site www.bcards.site;
# Redirect HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name bcards.site www.bcards.site;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/bcards.site/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/bcards.site/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
# Load balancer configuration
location / {
proxy_pass http://bcards_backend;
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;
# Connection settings
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
# Buffer settings
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
# Health check
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 30s;
}
# Health check endpoint
location /health {
access_log off;
proxy_pass http://bcards_backend;
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;
}
# Static files (if any)
location /static {
proxy_pass http://bcards_backend;
proxy_cache_valid 200 1h;
expires 1h;
add_header Cache-Control "public, immutable";
}
# Logging
access_log /var/log/nginx/bcards_access.log;
error_log /var/log/nginx/bcards_error.log;
}

50
nginx/nginx-local.conf Normal file
View File

@ -0,0 +1,50 @@
# NGINX Configuration for Local Development
# Configuração simplificada para testes locais
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Log format
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
# Upstream para desenvolvimento local
upstream bcards_local {
server bcards-test-app:8080;
}
server {
listen 80;
server_name localhost;
# Health check do NGINX
location = /nginx-health {
access_log off;
return 200 "nginx ok\n";
add_header Content-Type text/plain;
}
# Proxy para aplicação
location / {
proxy_pass http://bcards_local;
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;
# Timeouts para desenvolvimento
proxy_connect_timeout 10s;
proxy_send_timeout 10s;
proxy_read_timeout 10s;
}
}
}

127
nginx/nginx-test.conf Normal file
View File

@ -0,0 +1,127 @@
# NGINX Configuration for Local Testing
# Simula o ambiente de produção para testes locais
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Log format
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# Upstream configuration para load balancing
upstream bcards_backend {
least_conn;
server bcards-server1:8080 max_fails=3 fail_timeout=30s;
server bcards-server2:8080 max_fails=3 fail_timeout=30s;
keepalive 32;
}
# Rate limiting
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
server {
listen 80;
server_name localhost;
# Rate limiting
limit_req zone=general burst=20 nodelay;
# Health check do NGINX
location = /nginx-health {
access_log off;
return 200 "nginx ok\n";
add_header Content-Type text/plain;
}
# Backend health check
location = /backend-health {
access_log off;
proxy_pass http://bcards_backend/health;
proxy_connect_timeout 5s;
proxy_read_timeout 5s;
}
# Main application proxy
location / {
proxy_pass http://bcards_backend;
# Headers para proxy reverso
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;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# Timeouts
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
# Buffer settings
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
# Retry configuration
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 10s;
}
# Static files caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
proxy_pass http://bcards_backend;
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;
expires 1h;
add_header Cache-Control "public";
access_log off;
}
# Status page para monitoramento
location = /status {
access_log off;
return 200 "{\n \"status\": \"ok\",\n \"timestamp\": \"$time_iso8601\",\n \"server\": \"nginx-test\"\n}";
add_header Content-Type application/json;
}
# Error pages
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
# Monitoring endpoint
server {
listen 8081;
server_name localhost;
location /nginx_status {
stub_status on;
access_log off;
allow all;
}
}
}

239
nginx/nginx.conf Normal file
View File

@ -0,0 +1,239 @@
# NGINX Configuration for BCards Load Balancer
# Configuração principal para produção com SSL e load balancing
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
# Otimizações de performance
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
http {
# Configurações básicas
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging format
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
access_log /var/log/nginx/access.log main;
# Performance settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 20M;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
# Rate limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
# Upstream configuration for load balancing
upstream bcards_backend {
# Configuração de load balancing com health checks
least_conn;
# Servidores backend
server 172.18.0.2:8080 max_fails=3 fail_timeout=30s;
server 172.18.0.3:8080 max_fails=3 fail_timeout=30s;
# Manter conexões vivas
keepalive 32;
}
# Configuração para health checks dos backends
upstream bcards_health {
server 172.18.0.2:8080;
server 172.18.0.3:8080;
}
# Rate limiting
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
# Configuração SSL/TLS
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Security headers
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Redirect HTTP to HTTPS
server {
listen 80;
server_name bcards.site www.bcards.site;
# ACME challenge para Let's Encrypt
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# Redirect para HTTPS
location / {
return 301 https://$server_name$request_uri;
}
}
# Main HTTPS server configuration
server {
listen 443 ssl http2;
server_name bcards.site www.bcards.site;
# SSL certificates
ssl_certificate /etc/letsencrypt/live/bcards.site/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/bcards.site/privkey.pem;
# Trusted certificate for OCSP stapling
ssl_trusted_certificate /etc/letsencrypt/live/bcards.site/chain.pem;
ssl_stapling on;
ssl_stapling_verify on;
# DNS resolver
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Rate limiting
limit_req zone=general burst=20 nodelay;
# Health check endpoint (não passar pelo backend)
location = /nginx-health {
access_log off;
return 200 "nginx ok\n";
add_header Content-Type text/plain;
}
# Backend health check
location = /backend-health {
access_log off;
proxy_pass http://bcards_health/health;
proxy_connect_timeout 5s;
proxy_read_timeout 5s;
}
# Main application proxy
location / {
# Proxy para o backend
proxy_pass http://bcards_backend;
# Headers para proxy reverso
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;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# Timeouts
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
# Buffer settings
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
# Retry configuration
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 10s;
}
# Static files caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
proxy_pass http://bcards_backend;
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;
# Cache headers
expires 1y;
add_header Cache-Control "public, immutable";
# Disable logging for static files
access_log off;
}
# Error pages
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
# Servidor para monitoramento interno (apenas localhost)
server {
listen 127.0.0.1:8081;
server_name localhost;
# Status do NGINX
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
# Status detalhado dos upstreams
location /upstream_status {
access_log off;
allow 127.0.0.1;
deny all;
return 200 "Backend Status Check\n";
add_header Content-Type text/plain;
}
}
}
# Stream configuration for TCP load balancing (se necessário)
# stream {
# upstream tcp_backend {
# server 172.18.0.2:9000;
# server 172.18.0.3:9000;
# }
#
# server {
# listen 9000;
# proxy_pass tcp_backend;
# proxy_timeout 1s;
# proxy_responses 1;
# }
# }

148
nginx/setup-ssl.sh Normal file
View File

@ -0,0 +1,148 @@
#!/bin/bash
# Script para configurar SSL com Let's Encrypt para bcards.site
set -e
DOMAIN="bcards.site"
EMAIL="admin@bcards.site" # Altere para um email válido
# Cores para output
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m'
log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
}
error() {
echo -e "${RED}[ERROR] $1${NC}"
exit 1
}
warn() {
echo -e "${YELLOW}[WARNING] $1${NC}"
}
# Verificar se Docker está rodando
if ! docker info >/dev/null 2>&1; then
error "Docker não está rodando"
fi
# Criar diretórios necessários
log "Criando diretórios para SSL..."
sudo mkdir -p /etc/letsencrypt /var/www/certbot
# Criar configuração temporária do NGINX para validação
log "Criando configuração temporária do NGINX..."
cat > /tmp/nginx-temp.conf << EOF
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name $DOMAIN www.$DOMAIN;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://\$server_name\$request_uri;
}
}
}
EOF
# Parar NGINX se estiver rodando
log "Parando NGINX existente..."
docker stop bcards-nginx 2>/dev/null || true
docker rm bcards-nginx 2>/dev/null || true
# Iniciar NGINX temporário para validação
log "Iniciando NGINX temporário para validação Let's Encrypt..."
docker run -d \
--name nginx-temp \
-p 80:80 \
-v /tmp/nginx-temp.conf:/etc/nginx/nginx.conf:ro \
-v /var/www/certbot:/var/www/certbot \
nginx:alpine
# Aguardar NGINX inicializar
sleep 5
# Obter certificado SSL
log "Obtendo certificado SSL para $DOMAIN..."
docker run --rm \
-v /etc/letsencrypt:/etc/letsencrypt \
-v /var/www/certbot:/var/www/certbot \
certbot/certbot \
certonly \
--webroot \
--webroot-path=/var/www/certbot \
--email $EMAIL \
--agree-tos \
--no-eff-email \
-d $DOMAIN \
-d www.$DOMAIN
# Verificar se o certificado foi criado
if [ ! -f "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" ]; then
error "Falha ao obter certificado SSL"
fi
log "Certificado SSL obtido com sucesso!"
# Parar NGINX temporário
docker stop nginx-temp
docker rm nginx-temp
# Testar configuração NGINX principal
log "Testando configuração NGINX principal..."
docker run --rm \
-v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
-v /etc/letsencrypt:/etc/letsencrypt:ro \
nginx:alpine \
nginx -t
log "Configuração NGINX válida!"
# Criar script de renovação automática
log "Criando script de renovação automática..."
cat > /tmp/renew-ssl.sh << 'EOF'
#!/bin/bash
# Script de renovação automática de certificados SSL
docker run --rm \
-v /etc/letsencrypt:/etc/letsencrypt \
-v /var/www/certbot:/var/www/certbot \
certbot/certbot renew
# Recarregar NGINX se certificados foram renovados
if [ $? -eq 0 ]; then
docker exec bcards-nginx nginx -s reload
fi
EOF
sudo mv /tmp/renew-ssl.sh /usr/local/bin/renew-ssl.sh
sudo chmod +x /usr/local/bin/renew-ssl.sh
# Configurar cron para renovação automática
log "Configurando renovação automática..."
(crontab -l 2>/dev/null; echo "0 2 * * * /usr/local/bin/renew-ssl.sh >> /var/log/letsencrypt/renew.log 2>&1") | crontab -
# Criar diretório de logs
sudo mkdir -p /var/log/letsencrypt
log "SSL configurado com sucesso!"
log "Certificados localizados em: /etc/letsencrypt/live/$DOMAIN/"
log "Renovação automática configurada para executar diariamente às 02:00"
# Mostrar informações do certificado
log "Informações do certificado:"
sudo openssl x509 -in /etc/letsencrypt/live/$DOMAIN/fullchain.pem -text -noout | grep -A 2 "Validity"

33
redis/redis.conf Normal file
View File

@ -0,0 +1,33 @@
# Redis configuration for BCards
# Network
bind 0.0.0.0
port 6379
protected-mode no
# General
daemonize no
pidfile /var/run/redis_6379.pid
loglevel notice
logfile ""
# Memory
maxmemory 256mb
maxmemory-policy allkeys-lru
# Persistence
save 900 1
save 300 10
save 60 10000
# Security
# requirepass bcards123
# Performance
tcp-keepalive 300
timeout 0
# Append only mode
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec

337
scripts/cleanup.sh Normal file
View File

@ -0,0 +1,337 @@
#!/bin/bash
# BCards Cleanup Script
# Limpeza de containers antigos e recursos não utilizados
set -e
# Configurações
SERVER_IPS=("129.153.123.92" "129.146.116.218")
SSH_USER="ubuntu"
SERVICE_NAME="bcards-app"
# Cores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
}
error() {
echo -e "${RED}[ERROR] $1${NC}"
exit 1
}
warn() {
echo -e "${YELLOW}[WARNING] $1${NC}"
}
info() {
echo -e "${BLUE}[INFO] $1${NC}"
}
success() {
echo -e "${GREEN}[SUCCESS] $1${NC}"
}
# Função para executar comando em servidor remoto
run_remote() {
local server_ip=$1
local command=$2
ssh -o StrictHostKeyChecking=no ${SSH_USER}@${server_ip} "$command"
}
# Função para executar comando apenas no manager
run_manager() {
local command=$1
run_remote ${SERVER_IPS[0]} "$command"
}
# Verificar conectividade
check_connectivity() {
log "Verificando conectividade com servidores..."
for server_ip in "${SERVER_IPS[@]}"; do
if ! ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no ${SSH_USER}@${server_ip} "echo 'ok'" >/dev/null 2>&1; then
error "Não foi possível conectar ao servidor $server_ip"
fi
info "✓ Conectado: $server_ip"
done
}
# Mostrar uso atual de espaço
show_disk_usage() {
log "Verificando uso atual de disco..."
for server_ip in "${SERVER_IPS[@]}"; do
info "Servidor $server_ip:"
run_remote $server_ip "
echo ' Espaço total em disco:'
df -h / | tail -1 | awk '{print \" Total: \" \$2 \", Usado: \" \$3 \", Livre: \" \$4 \", Uso: \" \$5}'
echo ' Espaço usado pelo Docker:'
sudo docker system df 2>/dev/null || echo ' Docker não disponível'
"
done
echo ""
}
# Listar containers e imagens antes da limpeza
show_before_cleanup() {
log "Status antes da limpeza..."
for server_ip in "${SERVER_IPS[@]}"; do
info "Servidor $server_ip:"
echo " Containers:"
run_remote $server_ip "sudo docker ps -a --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.CreatedAt}}'" || true
echo " Imagens:"
run_remote $server_ip "sudo docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}'" | head -10 || true
echo ""
done
}
# Parar serviços específicos (se solicitado)
stop_services() {
if [[ "$1" == "--stop-services" ]]; then
log "Parando serviços BCards..."
if run_manager "sudo docker service ls --filter name=${SERVICE_NAME}" | grep -q "${SERVICE_NAME}"; then
run_manager "sudo docker service rm ${SERVICE_NAME}_app-server1 ${SERVICE_NAME}_app-server2" || true
info "Serviços BCards parados"
# Aguardar containers pararem
sleep 10
else
info "Nenhum serviço BCards encontrado"
fi
fi
}
# Limpeza de containers parados
cleanup_containers() {
log "Limpando containers parados..."
for server_ip in "${SERVER_IPS[@]}"; do
info "Limpando containers no servidor $server_ip..."
# Remover containers parados
local stopped_containers=$(run_remote $server_ip "sudo docker ps -aq --filter 'status=exited'")
if [ -n "$stopped_containers" ]; then
run_remote $server_ip "sudo docker rm \$(sudo docker ps -aq --filter 'status=exited')" || true
success " ✓ Containers parados removidos"
else
info " Nenhum container parado encontrado"
fi
# Remover containers órfãos
run_remote $server_ip "sudo docker container prune -f" || true
done
}
# Limpeza de imagens não utilizadas
cleanup_images() {
log "Limpando imagens não utilizadas..."
local keep_latest=${1:-true}
for server_ip in "${SERVER_IPS[@]}"; do
info "Limpando imagens no servidor $server_ip..."
if [[ "$keep_latest" == "true" ]]; then
# Remover apenas imagens dangling (sem tag)
run_remote $server_ip "sudo docker image prune -f" || true
success " ✓ Imagens dangling removidas"
else
# Remover todas as imagens não utilizadas
run_remote $server_ip "sudo docker image prune -af" || true
success " ✓ Todas as imagens não utilizadas removidas"
fi
done
}
# Limpeza de volumes não utilizados
cleanup_volumes() {
log "Limpando volumes não utilizados..."
for server_ip in "${SERVER_IPS[@]}"; do
info "Limpando volumes no servidor $server_ip..."
local unused_volumes=$(run_remote $server_ip "sudo docker volume ls -qf dangling=true")
if [ -n "$unused_volumes" ]; then
run_remote $server_ip "sudo docker volume prune -f" || true
success " ✓ Volumes não utilizados removidos"
else
info " Nenhum volume não utilizado encontrado"
fi
done
}
# Limpeza de redes não utilizadas
cleanup_networks() {
log "Limpando redes não utilizadas..."
for server_ip in "${SERVER_IPS[@]}"; do
info "Limpando redes no servidor $server_ip..."
run_remote $server_ip "sudo docker network prune -f" || true
success " ✓ Redes não utilizadas removidas"
done
}
# Limpeza de build cache
cleanup_build_cache() {
log "Limpando build cache..."
for server_ip in "${SERVER_IPS[@]}"; do
info "Limpando build cache no servidor $server_ip..."
run_remote $server_ip "sudo docker builder prune -f" || true
success " ✓ Build cache limpo"
done
}
# Limpeza de logs antigos
cleanup_logs() {
log "Limpando logs antigos..."
for server_ip in "${SERVER_IPS[@]}"; do
info "Limpando logs no servidor $server_ip..."
# Limpar logs do Docker (mais de 7 dias)
run_remote $server_ip "
sudo find /var/lib/docker/containers/ -name '*.log' -type f -mtime +7 -delete 2>/dev/null || true
sudo find /var/log/bcards/ -name '*.log' -type f -mtime +30 -delete 2>/dev/null || true
sudo journalctl --vacuum-time=7d 2>/dev/null || true
"
success " ✓ Logs antigos removidos"
done
}
# Limpeza do sistema operacional
cleanup_system() {
if [[ "$1" == "--system" ]]; then
log "Limpando sistema operacional..."
for server_ip in "${SERVER_IPS[@]}"; do
info "Limpando sistema no servidor $server_ip..."
run_remote $server_ip "
sudo apt-get autoremove -y 2>/dev/null || true
sudo apt-get autoclean 2>/dev/null || true
sudo apt-get clean 2>/dev/null || true
"
success " ✓ Sistema limpo"
done
fi
}
# Limpeza completa do Docker
full_docker_cleanup() {
if [[ "$1" == "--full" ]]; then
warn "ATENÇÃO: Limpeza completa irá remover TUDO (containers, imagens, volumes, redes)"
read -p "Tem certeza? Digite 'yes' para continuar: " confirm
if [[ "$confirm" == "yes" ]]; then
log "Executando limpeza completa do Docker..."
for server_ip in "${SERVER_IPS[@]}"; do
info "Limpeza completa no servidor $server_ip..."
run_remote $server_ip "
sudo docker system prune -af --volumes
"
success " ✓ Limpeza completa executada"
done
else
info "Limpeza completa cancelada"
fi
fi
}
# Mostrar estatísticas após limpeza
show_after_cleanup() {
log "Status após limpeza..."
for server_ip in "${SERVER_IPS[@]}"; do
info "Servidor $server_ip:"
echo " Espaço em disco:"
run_remote $server_ip "df -h / | tail -1 | awk '{print \" Total: \" \$2 \", Usado: \" \$3 \", Livre: \" \$4 \", Uso: \" \$5}'"
echo " Docker:"
run_remote $server_ip "sudo docker system df 2>/dev/null || echo ' Docker não disponível'"
echo " Containers ativos:"
local active_containers=$(run_remote $server_ip "sudo docker ps -q | wc -l")
echo " $active_containers containers rodando"
echo " Imagens:"
local images_count=$(run_remote $server_ip "sudo docker images -q | wc -l")
echo " $images_count imagens armazenadas"
echo ""
done
}
# Função principal
main() {
log "Iniciando limpeza da infraestrutura BCards..."
check_connectivity
show_disk_usage
show_before_cleanup
stop_services "$@"
cleanup_containers
cleanup_images "$2"
cleanup_volumes
cleanup_networks
cleanup_build_cache
cleanup_logs
cleanup_system "$@"
full_docker_cleanup "$@"
show_after_cleanup
success "Limpeza concluída!"
}
# Função de ajuda
show_help() {
echo "BCards Cleanup Script"
echo ""
echo "Usage: $0 [OPTIONS]"
echo ""
echo "OPTIONS:"
echo " --stop-services Para os serviços BCards antes da limpeza"
echo " --system Inclui limpeza do sistema operacional (apt clean, etc)"
echo " --full Limpeza completa (REMOVE TUDO - containers, imagens, volumes)"
echo " --keep-all-images Mantém todas as imagens (remove apenas dangling)"
echo " --help Mostra esta ajuda"
echo ""
echo "Operações de limpeza:"
echo "- Remove containers parados"
echo "- Remove imagens não utilizadas"
echo "- Remove volumes órfãos"
echo "- Remove redes não utilizadas"
echo "- Limpa build cache do Docker"
echo "- Remove logs antigos"
echo ""
echo "Examples:"
echo " $0 # Limpeza básica"
echo " $0 --stop-services # Para serviços e limpa"
echo " $0 --system # Inclui limpeza do sistema"
echo " $0 --full # Limpeza completa (cuidado!)"
echo ""
echo "Servers: ${SERVER_IPS[*]}"
}
# Parse argumentos
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
show_help
exit 0
fi
# Executar função principal
main "$@"

View File

@ -0,0 +1,342 @@
#!/bin/bash
# BCards Deploy Script - Containers Independentes
# Deploy automatizado da aplicação nos servidores OCI com Docker Compose
set -e
# Configurações
SERVER_IPS=("141.148.162.114" "129.146.116.218")
SSH_USER="ubuntu"
REGISTRY="registry.redecarneir.us"
APP_NAME="bcards-test-app"
CONTAINER_NAME="bcards-app"
NETWORK_NAME="bcards-network"
# Versão da aplicação (pode ser passada como parâmetro)
VERSION=${1:-"latest"}
# Cores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
}
error() {
echo -e "${RED}[ERROR] $1${NC}"
exit 1
}
warn() {
echo -e "${YELLOW}[WARNING] $1${NC}"
}
info() {
echo -e "${BLUE}[INFO] $1${NC}"
}
# Função para executar comando em servidor remoto
run_remote() {
local server_ip=$1
local command=$2
ssh -o StrictHostKeyChecking=no ${SSH_USER}@${server_ip} "$command"
}
# Verificar conectividade com servidores
check_connectivity() {
log "Verificando conectividade com os servidores..."
for server_ip in "${SERVER_IPS[@]}"; do
if ! ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no ${SSH_USER}@${server_ip} "echo 'ok'" >/dev/null 2>&1; then
error "Não foi possível conectar ao servidor $server_ip"
fi
info "✓ Servidor $server_ip conectado"
done
}
# Verificar se Docker está ativo
check_docker() {
log "Verificando status do Docker..."
for server_ip in "${SERVER_IPS[@]}"; do
if ! run_remote $server_ip "sudo docker info" >/dev/null 2>&1; then
error "Docker não está ativo no servidor $server_ip. Execute setup-servers.sh primeiro."
fi
info "✓ Docker está ativo no servidor $server_ip"
done
}
# Build da aplicação local (se necessário)
build_application() {
log "Building aplicação localmente..."
cd test-app
# Build da imagem Docker
info "Building imagem Docker..."
docker build -t ${APP_NAME}:${VERSION} .
# Tag para registry
docker tag ${APP_NAME}:${VERSION} ${REGISTRY}/${APP_NAME}:${VERSION}
docker tag ${APP_NAME}:${VERSION} ${REGISTRY}/${APP_NAME}:latest
info "✓ Aplicação construída com sucesso"
cd ..
}
# Push da imagem para registry
push_to_registry() {
log "Enviando imagem para registry..."
# Push da imagem
docker push ${REGISTRY}/${APP_NAME}:${VERSION}
docker push ${REGISTRY}/${APP_NAME}:latest
info "✓ Imagem enviada para registry"
}
# Deploy em containers independentes
deploy_containers() {
log "Fazendo deploy dos containers independentes..."
local server_count=1
for server_ip in "${SERVER_IPS[@]}"; do
info "Deploy no servidor $server_ip (Server $server_count)..."
# Criar docker-compose específico para cada servidor
cat > /tmp/docker-compose-server${server_count}.yml << EOF
version: '3.8'
services:
bcards-app:
image: ${REGISTRY}/${APP_NAME}:${VERSION}
container_name: ${CONTAINER_NAME}-server${server_count}
restart: unless-stopped
ports:
- "8080:8080"
environment:
- ASPNETCORE_ENVIRONMENT=Production
- SERVER_NAME=BCards Server ${server_count}
- SERVER_COLOR=$([ $server_count -eq 1 ] && echo "#28a745" || echo "#007bff")
networks:
- bcards-network
mem_limit: 512m
mem_reservation: 256m
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
bcards-network:
external: true
EOF
# Copiar docker-compose para o servidor
scp /tmp/docker-compose-server${server_count}.yml ${SSH_USER}@${server_ip}:~/bcards/docker-compose.yml
# Pull da imagem
info "Fazendo pull da imagem no servidor $server_ip..."
run_remote $server_ip "sudo docker pull ${REGISTRY}/${APP_NAME}:${VERSION}"
# Parar container antigo se existir
run_remote $server_ip "sudo docker compose -f ~/bcards/docker-compose.yml down" || true
# Iniciar novo container
info "Iniciando container no servidor $server_ip..."
run_remote $server_ip "cd ~/bcards && sudo docker compose up -d"
((server_count++))
done
info "✓ Containers deployados com sucesso"
}
# Aguardar containers ficarem saudáveis
wait_for_health() {
log "Aguardando containers ficarem saudáveis..."
local max_attempts=30
local attempt=1
while [ $attempt -le $max_attempts ]; do
local healthy_containers=0
for server_ip in "${SERVER_IPS[@]}"; do
local container_health=$(run_remote $server_ip "sudo docker inspect --format='{{.State.Health.Status}}' ${CONTAINER_NAME}-server* 2>/dev/null" | grep -c "healthy" || echo "0")
((healthy_containers += container_health))
done
if [ "$healthy_containers" -eq "2" ]; then
info "✓ Todos os containers estão saudáveis"
return 0
fi
info "Tentativa $attempt/$max_attempts - Aguardando containers ficarem saudáveis... ($healthy_containers/2)"
sleep 10
((attempt++))
done
warn "Timeout aguardando containers ficarem saudáveis"
check_container_status
return 1
}
# Verificar saúde dos containers
check_container_status() {
log "Verificando saúde dos containers..."
local server_count=1
for server_ip in "${SERVER_IPS[@]}"; do
info "Status do servidor $server_ip:"
run_remote $server_ip "sudo docker ps --filter name=${CONTAINER_NAME}"
info "Health check do servidor $server_ip:"
run_remote $server_ip "sudo docker inspect --format='{{.State.Health.Status}}' ${CONTAINER_NAME}-server${server_count} 2>/dev/null" || true
info "Logs dos últimos 50 eventos do servidor $server_ip:"
run_remote $server_ip "sudo docker logs --tail 50 ${CONTAINER_NAME}-server${server_count}" || true
((server_count++))
done
}
# Teste de conectividade externa
test_connectivity() {
log "Testando conectividade externa..."
# Teste HTTP
if curl -f -s "http://bcards.site/health" >/dev/null 2>&1; then
info "✓ HTTP health check passou"
else
warn "HTTP health check falhou"
fi
# Teste HTTPS (se SSL estiver configurado)
if curl -f -s "https://bcards.site/health" >/dev/null 2>&1; then
info "✓ HTTPS health check passou"
else
warn "HTTPS health check falhou ou SSL não configurado"
fi
}
# Rollback em caso de falha
rollback() {
log "Executando rollback..."
for server_ip in "${SERVER_IPS[@]}"; do
info "Rollback no servidor $server_ip..."
# Parar container atual
run_remote $server_ip "sudo docker compose -f ~/bcards/docker-compose.yml down" || true
# Tentar usar imagem latest anterior
run_remote $server_ip "sudo docker pull ${REGISTRY}/${APP_NAME}:latest"
# Atualizar docker-compose para usar latest
run_remote $server_ip "sed -i 's/:${VERSION}/:latest/g' ~/bcards/docker-compose.yml"
# Reiniciar com versão anterior
run_remote $server_ip "cd ~/bcards && sudo docker compose up -d"
done
info "Rollback executado"
}
# Limpeza de imagens antigas
cleanup_old_images() {
log "Limpando imagens antigas..."
for server_ip in "${SERVER_IPS[@]}"; do
info "Limpando imagens no servidor $server_ip..."
run_remote $server_ip "
sudo docker image prune -f
sudo docker container prune -f
"
done
info "✓ Limpeza concluída"
}
# Função principal
main() {
log "Iniciando deploy para versão $VERSION..."
# Verificações pré-deploy
check_connectivity
check_docker
# Build e push
if [[ "$2" != "--skip-build" ]]; then
build_application
push_to_registry
else
info "Pulando build (--skip-build especificado)"
fi
# Deploy
deploy_containers
# Verificações pós-deploy
if wait_for_health; then
check_container_status
test_connectivity
cleanup_old_images
log "Deploy concluído com sucesso!"
info "Aplicação disponível em: http://bcards.site"
info "Health check: http://bcards.site/health"
else
error "Deploy falhou - serviços não ficaram saudáveis"
if [[ "$3" == "--auto-rollback" ]]; then
rollback
fi
fi
}
# Mostrar help
show_help() {
echo "BCards Deploy Script"
echo ""
echo "Usage: $0 [VERSION] [OPTIONS]"
echo ""
echo "VERSION:"
echo " Versão da aplicação para deploy (default: latest)"
echo ""
echo "OPTIONS:"
echo " --skip-build Pula o build local da aplicação"
echo " --auto-rollback Executa rollback automático em caso de falha"
echo " --help Mostra esta ajuda"
echo ""
echo "Examples:"
echo " $0 # Deploy versão latest"
echo " $0 v1.2.3 # Deploy versão específica"
echo " $0 latest --skip-build # Deploy sem rebuild"
echo ""
echo "Servers: ${SERVER_IPS[*]}"
echo "Registry: $REGISTRY"
}
# Parse argumentos
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
show_help
exit 0
fi
# Executar função principal
main "$@"

207
scripts/fix-nginx-ssl.sh Normal file
View File

@ -0,0 +1,207 @@
#!/bin/bash
# Fix NGINX Load Balancer SSL Configuration
# Corrige problemas de SSL do NGINX Load Balancer
set -e
LOADBALANCER_IP="141.148.162.114"
SSH_USER="ubuntu"
DOMAIN="bcards.site"
# Cores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
}
error() {
echo -e "${RED}[ERROR] $1${NC}"
exit 1
}
warn() {
echo -e "${YELLOW}[WARNING] $1${NC}"
}
info() {
echo -e "${BLUE}[INFO] $1${NC}"
}
run_remote() {
local server_ip=$1
local command=$2
ssh -o StrictHostKeyChecking=no ${SSH_USER}@${server_ip} "$command"
}
# Etapa 1: Configurar NGINX apenas com HTTP
fix_nginx_http() {
log "Configurando NGINX apenas com HTTP..."
# Copiar configuração HTTP
scp nginx/nginx-loadbalancer-http.conf ${SSH_USER}@${LOADBALANCER_IP}:/tmp/
run_remote $LOADBALANCER_IP "
# Parar NGINX
sudo systemctl stop nginx || true
# Backup da configuração atual
sudo mkdir -p /etc/nginx/backup
sudo cp /etc/nginx/sites-available/bcards /etc/nginx/backup/bcards-ssl.bak 2>/dev/null || true
# Instalar configuração HTTP
sudo cp /tmp/nginx-loadbalancer-http.conf /etc/nginx/sites-available/bcards
# Criar diretório para ACME challenge
sudo mkdir -p /var/www/html
sudo chown www-data:www-data /var/www/html
# Testar configuração
sudo nginx -t
# Iniciar NGINX
sudo systemctl start nginx
sudo systemctl enable nginx
"
info "✓ NGINX configurado apenas com HTTP"
}
# Etapa 2: Testar se o domínio está acessível
test_domain_connectivity() {
log "Testando conectividade do domínio..."
# Primeiro, verificar se os backends estão rodando
info "Verificando se os backends estão rodando..."
for backend in "129.153.123.92:8080" "129.146.116.218:8080"; do
if curl -s --connect-timeout 5 "http://$backend/health" >/dev/null 2>&1; then
info "✓ Backend $backend está respondendo"
else
warn "Backend $backend não está respondendo"
fi
done
# Testar conectividade local do load balancer
info "Testando NGINX localmente no load balancer..."
run_remote $LOADBALANCER_IP "
# Testar localmente
curl -s -o /dev/null -w 'Status: %{http_code}\n' http://localhost/health || echo 'Local test failed'
"
# Aguardar propagação DNS
info "Aguardando 30 segundos para propagação..."
sleep 30
# Testar acesso externo
info "Testando acesso externo ao domínio..."
local status=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 10 "http://$DOMAIN/health" 2>/dev/null || echo "000")
if [ "$status" = "200" ]; then
info "✓ Domínio $DOMAIN está acessível"
return 0
else
warn "Domínio $DOMAIN não está acessível (status: $status)"
# Diagnóstico adicional
info "Executando diagnóstico..."
nslookup $DOMAIN || true
info "Tentando ping..."
ping -c 3 $DOMAIN || true
return 1
fi
}
# Etapa 3: Configurar SSL (apenas se domínio estiver acessível)
setup_ssl_proper() {
log "Configurando SSL com Let's Encrypt..."
run_remote $LOADBALANCER_IP "
# Obter certificado SSL apenas para domínio principal (sem www)
sudo certbot certonly --webroot -w /var/www/html -d $DOMAIN --non-interactive --agree-tos --email admin@$DOMAIN
# Verificar se certificado foi criado
if [ ! -f /etc/letsencrypt/live/$DOMAIN/fullchain.pem ]; then
echo 'Certificado não foi criado!'
exit 1
fi
"
# Agora configurar NGINX com SSL
scp nginx/nginx-loadbalancer.conf ${SSH_USER}@${LOADBALANCER_IP}:/tmp/
run_remote $LOADBALANCER_IP "
# Atualizar configuração para SSL (remover www do server_name)
sed -i 's/server_name bcards.site www.bcards.site;/server_name bcards.site;/g' /tmp/nginx-loadbalancer.conf
# Aplicar configuração SSL
sudo cp /tmp/nginx-loadbalancer.conf /etc/nginx/sites-available/bcards
# Testar configuração
sudo nginx -t
# Reload NGINX
sudo systemctl reload nginx
# Configurar renovação automática (apenas se não existir)
if ! crontab -l 2>/dev/null | grep -q certbot; then
echo '0 2 * * * certbot renew --quiet && systemctl reload nginx' | sudo crontab -
fi
"
info "✓ SSL configurado com sucesso"
}
# Função principal
main() {
log "Corrigindo configuração NGINX SSL para BCards..."
# Etapa 1: Configurar HTTP primeiro
fix_nginx_http
# Etapa 2: Testar conectividade
if test_domain_connectivity; then
# Etapa 3: Se domínio estiver acessível, configurar SSL
if [[ "$1" == "--ssl" ]]; then
setup_ssl_proper
info "Acesso: https://$DOMAIN"
else
info "SSL não configurado. Use --ssl para configurar."
info "Acesso: http://$DOMAIN"
fi
else
warn "Domínio não está acessível. Verifique:"
warn "1. DNS está apontando para $LOADBALANCER_IP"
warn "2. Backends estão rodando nas portas 8080"
warn "3. Firewall permite tráfego HTTP (porta 80)"
info "Por enquanto, acesso apenas local: http://$LOADBALANCER_IP"
fi
log "Correção concluída!"
}
# Mostrar ajuda
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
echo "Fix NGINX Load Balancer SSL Configuration"
echo ""
echo "Usage: $0 [OPTIONS]"
echo ""
echo "OPTIONS:"
echo " --ssl Configurar SSL após verificar conectividade"
echo " --help Mostrar esta ajuda"
echo ""
echo "Este script:"
echo "1. Configura NGINX apenas com HTTP primeiro"
echo "2. Testa se o domínio está acessível"
echo "3. Só configura SSL se domínio estiver funcionando"
echo ""
exit 0
fi
# Executar função principal
main "$@"

View File

@ -0,0 +1,196 @@
#!/bin/bash
# Setup NGINX Load Balancer for BCards
# Configura NGINX para fazer load balance entre os servidores
set -e
LOADBALANCER_IP="141.148.162.114" # Usar primeiro servidor como load balancer
SSH_USER="ubuntu"
DOMAIN="bcards.site"
# Cores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
}
error() {
echo -e "${RED}[ERROR] $1${NC}"
exit 1
}
warn() {
echo -e "${YELLOW}[WARNING] $1${NC}"
}
info() {
echo -e "${BLUE}[INFO] $1${NC}"
}
# Função para executar comando em servidor remoto
run_remote() {
local server_ip=$1
local command=$2
ssh -o StrictHostKeyChecking=no ${SSH_USER}@${server_ip} "$command"
}
# Verificar conectividade
check_connectivity() {
log "Verificando conectividade com o load balancer..."
if ! ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no ${SSH_USER}@${LOADBALANCER_IP} "echo 'ok'" >/dev/null 2>&1; then
error "Não foi possível conectar ao servidor $LOADBALANCER_IP"
fi
info "✓ Conectividade OK"
}
# Configurar NGINX como load balancer
setup_nginx_loadbalancer() {
log "Configurando NGINX Load Balancer..."
# Copiar arquivo de configuração
scp nginx/nginx-loadbalancer.conf ${SSH_USER}@${LOADBALANCER_IP}:/tmp/
# Configurar NGINX
run_remote $LOADBALANCER_IP "
# Backup da configuração atual
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.backup 2>/dev/null || true
# Instalar configuração do load balancer
sudo cp /tmp/nginx-loadbalancer.conf /etc/nginx/sites-available/bcards
sudo ln -sf /etc/nginx/sites-available/bcards /etc/nginx/sites-enabled/
# Remover site padrão
sudo rm -f /etc/nginx/sites-enabled/default
# Testar configuração
sudo nginx -t
# Restart NGINX
sudo systemctl restart nginx
sudo systemctl enable nginx
"
info "✓ NGINX Load Balancer configurado"
}
# Configurar SSL com Let's Encrypt
setup_ssl() {
log "Configurando SSL com Let's Encrypt..."
run_remote $LOADBALANCER_IP "
# Parar NGINX temporariamente para certbot
sudo systemctl stop nginx
# Obter certificado SSL
sudo certbot certonly --standalone -d $DOMAIN -d www.$DOMAIN --non-interactive --agree-tos --email admin@$DOMAIN
# Restart NGINX
sudo systemctl start nginx
# Configurar renovação automática
echo '0 2 * * * root certbot renew --quiet && systemctl reload nginx' | sudo tee -a /etc/crontab
"
info "✓ SSL configurado com Let's Encrypt"
}
# Configurar firewall para load balancer
configure_firewall() {
log "Configurando firewall para load balancer..."
run_remote $LOADBALANCER_IP "
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
"
info "✓ Firewall configurado"
}
# Testar load balancer
test_loadbalancer() {
log "Testando load balancer..."
# Teste HTTP (deve redirecionar para HTTPS)
info "Testando redirecionamento HTTP para HTTPS..."
local http_status=$(curl -s -o /dev/null -w "%{http_code}" -L http://$DOMAIN/health)
if [ "$http_status" = "200" ]; then
info "✓ Redirecionamento HTTP->HTTPS funcionando"
else
warn "HTTP test retornou status: $http_status"
fi
# Teste HTTPS
info "Testando HTTPS..."
local https_status=$(curl -s -o /dev/null -w "%{http_code}" -k https://$DOMAIN/health)
if [ "$https_status" = "200" ]; then
info "✓ HTTPS funcionando"
else
warn "HTTPS test retornou status: $https_status"
fi
# Mostrar logs do NGINX
info "Últimos logs do NGINX:"
run_remote $LOADBALANCER_IP "sudo tail -n 20 /var/log/nginx/bcards_access.log" || true
run_remote $LOADBALANCER_IP "sudo tail -n 10 /var/log/nginx/bcards_error.log" || true
}
# Função principal
main() {
log "Configurando NGINX Load Balancer para BCards..."
check_connectivity
setup_nginx_loadbalancer
configure_firewall
if [[ "$1" == "--ssl" ]]; then
setup_ssl
else
warn "SSL não configurado. Use --ssl para configurar automaticamente."
info "Para configurar SSL manualmente:"
info " sudo certbot --nginx -d $DOMAIN -d www.$DOMAIN"
fi
test_loadbalancer
log "Setup do Load Balancer concluído!"
info "Load Balancer: $LOADBALANCER_IP"
info "Domain: $DOMAIN"
info "Backends: 129.153.123.92:8080, 129.146.116.218:8080"
if [[ "$1" == "--ssl" ]]; then
info "Acesso: https://$DOMAIN"
else
info "Acesso: http://$DOMAIN (configurar SSL depois)"
fi
}
# Mostrar help
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
echo "Setup NGINX Load Balancer for BCards"
echo ""
echo "Usage: $0 [OPTIONS]"
echo ""
echo "OPTIONS:"
echo " --ssl Configurar SSL automaticamente com Let's Encrypt"
echo " --help Mostrar esta ajuda"
echo ""
echo "Examples:"
echo " $0 # Setup sem SSL"
echo " $0 --ssl # Setup com SSL automático"
echo ""
echo "Load Balancer: $LOADBALANCER_IP"
echo "Domain: $DOMAIN"
exit 0
fi
# Executar função principal
main "$@"

206
scripts/setup-servers.sh Normal file
View File

@ -0,0 +1,206 @@
#!/bin/bash
# BCards Infrastructure Setup Script
# Configura os servidores OCI para o ambiente de produção
set -e
SERVER_IPS=("141.148.162.114" "129.146.116.218")
SSH_USER="ubuntu"
# Cores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
}
error() {
echo -e "${RED}[ERROR] $1${NC}"
exit 1
}
warn() {
echo -e "${YELLOW}[WARNING] $1${NC}"
}
# Função para executar comando em servidor remoto
run_remote() {
local server_ip=$1
local command=$2
ssh -o StrictHostKeyChecking=no ${SSH_USER}@${server_ip} "$command"
}
# Instalar Docker se não existir
install_docker() {
local server_ip=$1
log "Verificando Docker no servidor $server_ip..."
if run_remote $server_ip "command -v docker >/dev/null 2>&1"; then
log "Docker já está instalado no servidor $server_ip"
else
log "Instalando Docker no servidor $server_ip..."
run_remote $server_ip "
sudo apt-get update &&
sudo apt-get install -y ca-certificates curl gnupg lsb-release &&
sudo mkdir -p /etc/apt/keyrings &&
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg &&
echo \"deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \$(lsb_release -cs) stable\" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null &&
sudo apt-get update &&
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin &&
sudo usermod -aG docker $SSH_USER
"
log "Docker instalado com sucesso no servidor $server_ip"
fi
}
# Configurar NGINX para load balancing
setup_nginx() {
local server_ip=$1
log "Configurando NGINX no servidor $server_ip..."
run_remote $server_ip "
sudo apt-get install -y nginx &&
sudo systemctl enable nginx &&
sudo systemctl start nginx
"
log "NGINX configurado no servidor $server_ip"
}
# Criar redes Docker locais
setup_docker_networks() {
local server_ip=$1
log "Configurando redes Docker no servidor $server_ip..."
run_remote $server_ip "
sudo docker network create --driver bridge bcards-network 2>/dev/null || true
"
}
# Configurar firewall básico
configure_firewall() {
local server_ip=$1
log "Configurando firewall no servidor $server_ip..."
run_remote $server_ip "
sudo ufw --force reset &&
sudo ufw default deny incoming &&
sudo ufw default allow outgoing &&
sudo ufw allow ssh &&
sudo ufw allow 80/tcp &&
sudo ufw allow 443/tcp &&
sudo ufw --force enable
"
log "Firewall configurado no servidor $server_ip"
}
# Instalar utilitários essenciais
install_utilities() {
local server_ip=$1
log "Instalando utilitários no servidor $server_ip..."
run_remote $server_ip "
sudo apt-get update &&
sudo apt-get install -y \
curl \
wget \
git \
htop \
nano \
unzip \
jq \
certbot \
python3-certbot-nginx
"
}
# Criar diretórios necessários
create_directories() {
local server_ip=$1
log "Criando diretórios no servidor $server_ip..."
run_remote $server_ip "
mkdir -p ~/bcards/{nginx,logs,data,ssl} &&
sudo mkdir -p /var/log/bcards &&
sudo chown -R $SSH_USER:$SSH_USER /var/log/bcards
"
}
# Configurar log rotation
setup_log_rotation() {
local server_ip=$1
log "Configurando rotação de logs no servidor $server_ip..."
run_remote $server_ip "
sudo tee /etc/logrotate.d/bcards > /dev/null <<EOF
/var/log/bcards/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 644 $SSH_USER $SSH_USER
}
EOF
"
}
# Função principal
main() {
log "Iniciando setup da infraestrutura BCards..."
# Verificar conectividade com os servidores
for server_ip in "${SERVER_IPS[@]}"; do
log "Testando conectividade com $server_ip..."
if ! ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no ${SSH_USER}@${server_ip} "echo 'Conectado com sucesso'" >/dev/null 2>&1; then
error "Não foi possível conectar ao servidor $server_ip"
fi
done
# Setup em cada servidor
for server_ip in "${SERVER_IPS[@]}"; do
log "Configurando servidor $server_ip..."
install_utilities $server_ip
install_docker $server_ip
configure_firewall $server_ip
create_directories $server_ip
setup_log_rotation $server_ip
setup_docker_networks $server_ip
setup_nginx $server_ip
done
log "Setup completo! Servidores prontos para deploy."
log "Servidor 1: ${SERVER_IPS[0]}"
log "Servidor 2: ${SERVER_IPS[1]}"
log "Cada servidor rodará containers Docker independentes"
log "NGINX fará load balance entre os servidores"
}
# Verificar se o script está sendo executado com argumentos
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
echo "BCards Infrastructure Setup Script"
echo "Usage: $0"
echo ""
echo "Este script configura os servidores OCI para o ambiente BCards:"
echo "- Instala Docker e utilitários"
echo "- Configura Docker Swarm"
echo "- Configura firewall básico"
echo "- Cria estrutura de diretórios"
echo ""
echo "Servidores configurados:"
for server_ip in "${SERVER_IPS[@]}"; do
echo " - $server_ip"
done
exit 0
fi
# Executar função principal
main "$@"

View File

@ -0,0 +1,374 @@
#!/bin/bash
# BCards Connectivity Test Script
# Testa conectividade e saúde dos serviços
set -e
# Configurações
SERVER_IPS=("141.148.162.114" "129.146.116.218")
SSH_USER="ubuntu"
DOMAIN="bcards.site"
SERVICE_NAME="bcards-app"
# Cores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
}
error() {
echo -e "${RED}[ERROR] $1${NC}"
}
warn() {
echo -e "${YELLOW}[WARNING] $1${NC}"
}
info() {
echo -e "${BLUE}[INFO] $1${NC}"
}
success() {
echo -e "${GREEN}[SUCCESS] $1${NC}"
}
# Função para executar comando em servidor remoto
run_remote() {
local server_ip=$1
local command=$2
ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no ${SSH_USER}@${server_ip} "$command" 2>/dev/null
}
# Teste de conectividade SSH
test_ssh_connectivity() {
log "Testando conectividade SSH..."
local passed=0
local total=${#SERVER_IPS[@]}
for server_ip in "${SERVER_IPS[@]}"; do
if run_remote $server_ip "echo 'SSH OK'" >/dev/null 2>&1; then
success "✓ SSH conectado: $server_ip"
((passed++))
else
error "✗ SSH falhou: $server_ip"
fi
done
echo ""
info "SSH Connectivity: $passed/$total servidores"
}
# Teste de status do Docker
test_docker_status() {
log "Testando status do Docker..."
local passed=0
local total=${#SERVER_IPS[@]}
for server_ip in "${SERVER_IPS[@]}"; do
if run_remote $server_ip "sudo docker info" >/dev/null 2>&1; then
success "✓ Docker ativo: $server_ip"
((passed++))
else
error "✗ Docker inativo: $server_ip"
fi
done
echo ""
info "Docker Status: $passed/$total servidores"
}
# Teste de status do Docker Swarm
test_swarm_status() {
log "Testando status do Docker Swarm..."
local manager_ip=${SERVER_IPS[0]}
if run_remote $manager_ip "sudo docker node ls" >/dev/null 2>&1; then
success "✓ Docker Swarm ativo"
# Mostrar status dos nodes
info "Status dos nodes:"
run_remote $manager_ip "sudo docker node ls" || true
else
error "✗ Docker Swarm inativo"
fi
echo ""
}
# Teste de serviços Docker
test_docker_services() {
log "Testando serviços Docker..."
local manager_ip=${SERVER_IPS[0]}
if run_remote $manager_ip "sudo docker service ls" >/dev/null 2>&1; then
info "Serviços ativos:"
run_remote $manager_ip "sudo docker service ls" || true
# Verificar serviços específicos do BCards
if run_remote $manager_ip "sudo docker service ls --filter name=${SERVICE_NAME}" | grep -q "${SERVICE_NAME}"; then
success "✓ Serviços BCards encontrados"
# Status detalhado dos serviços BCards
info "Status detalhado dos serviços BCards:"
run_remote $manager_ip "sudo docker service ps ${SERVICE_NAME}_app-server1 --no-trunc" || true
run_remote $manager_ip "sudo docker service ps ${SERVICE_NAME}_app-server2 --no-trunc" || true
else
warn "Serviços BCards não encontrados"
fi
else
error "✗ Falha ao listar serviços Docker"
fi
echo ""
}
# Teste de health checks internos
test_internal_health() {
log "Testando health checks internos..."
for server_ip in "${SERVER_IPS[@]}"; do
info "Testando servidor $server_ip..."
# Verificar se há containers rodando
local containers=$(run_remote $server_ip "sudo docker ps --filter status=running --format '{{.Names}}'" | wc -l)
info " Containers rodando: $containers"
# Teste de conectividade de rede interna
if run_remote $server_ip "sudo docker network ls | grep bcards" >/dev/null 2>&1; then
success " ✓ Rede bcards-network existe"
else
warn " ✗ Rede bcards-network não encontrada"
fi
done
echo ""
}
# Teste de conectividade externa HTTP/HTTPS
test_external_connectivity() {
log "Testando conectividade externa..."
# Teste HTTP
info "Testando HTTP..."
if curl -f -s --max-time 10 "http://$DOMAIN/nginx-health" >/dev/null 2>&1; then
success "✓ HTTP funcionando (http://$DOMAIN)"
else
error "✗ HTTP falhou (http://$DOMAIN)"
fi
# Teste HTTPS
info "Testando HTTPS..."
if curl -f -s --max-time 10 "https://$DOMAIN/nginx-health" >/dev/null 2>&1; then
success "✓ HTTPS funcionando (https://$DOMAIN)"
else
warn "✗ HTTPS falhou (https://$DOMAIN)"
fi
# Teste health check da aplicação
info "Testando health check da aplicação..."
if curl -f -s --max-time 10 "http://$DOMAIN/health" >/dev/null 2>&1; then
success "✓ Health check da aplicação passou"
else
error "✗ Health check da aplicação falhou"
fi
echo ""
}
# Teste de load balancing
test_load_balancing() {
log "Testando load balancing..."
local servers_found=()
for i in {1..10}; do
local response=$(curl -s --max-time 5 "http://$DOMAIN/server-info" 2>/dev/null | jq -r '.ServerName' 2>/dev/null || echo "Error")
if [[ "$response" != "Error" && "$response" != "null" ]]; then
servers_found+=("$response")
fi
sleep 0.5
done
if [ ${#servers_found[@]} -gt 0 ]; then
local unique_servers=$(printf '%s\n' "${servers_found[@]}" | sort -u | wc -l)
info "Servidores detectados em 10 requests:"
printf '%s\n' "${servers_found[@]}" | sort | uniq -c
if [ $unique_servers -gt 1 ]; then
success "✓ Load balancing funcionando ($unique_servers servidores diferentes)"
else
warn "Load balancing pode não estar funcionando (apenas 1 servidor detectado)"
fi
else
error "✗ Falha ao testar load balancing"
fi
echo ""
}
# Teste de DNS
test_dns_resolution() {
log "Testando resolução DNS..."
if nslookup $DOMAIN >/dev/null 2>&1; then
local ip=$(nslookup $DOMAIN | grep -A1 "Name:" | tail -1 | awk '{print $2}')
success "✓ DNS funcionando: $DOMAIN -> $ip"
else
error "✗ Falha na resolução DNS para $DOMAIN"
fi
echo ""
}
# Teste de certificado SSL
test_ssl_certificate() {
log "Testando certificado SSL..."
if command -v openssl >/dev/null 2>&1; then
local ssl_info=$(echo | timeout 10 openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -dates 2>/dev/null)
if [ $? -eq 0 ]; then
success "✓ Certificado SSL válido"
info "Informações do certificado:"
echo "$ssl_info" | sed 's/^/ /'
else
warn "Certificado SSL não encontrado ou inválido"
fi
else
warn "OpenSSL não disponível para teste de certificado"
fi
echo ""
}
# Mostrar métricas de performance
show_performance_metrics() {
log "Coletando métricas de performance..."
for server_ip in "${SERVER_IPS[@]}"; do
info "Métricas do servidor $server_ip:"
# CPU e Memória
local cpu_mem=$(run_remote $server_ip "top -bn1 | grep 'Cpu(s)' | awk '{print \$2}' | awk -F'%' '{print \$1}'; free -m | awk 'NR==2{printf \"%.1f%%\", \$3*100/\$2}'")
info " CPU/Memória: $cpu_mem"
# Disk usage
local disk=$(run_remote $server_ip "df -h / | awk 'NR==2 {print \$5}'")
info " Uso do disco: $disk"
# Docker stats
local docker_containers=$(run_remote $server_ip "sudo docker ps --format 'table {{.Names}}\t{{.Status}}' | tail -n +2 | wc -l")
info " Containers Docker ativos: $docker_containers"
done
echo ""
}
# Gerar relatório detalhado
generate_report() {
local timestamp=$(date +'%Y-%m-%d %H:%M:%S')
local report_file="connectivity-report-$(date +'%Y%m%d_%H%M%S').txt"
log "Gerando relatório detalhado..."
{
echo "BCards Infrastructure Connectivity Report"
echo "Generated: $timestamp"
echo "========================================"
echo ""
echo "Infrastructure Overview:"
echo "- Domain: $DOMAIN"
echo "- Servers: ${SERVER_IPS[*]}"
echo "- Service: $SERVICE_NAME"
echo ""
echo "Test Results Summary:"
echo "- SSH Connectivity: $(test_ssh_connectivity 2>&1 | grep -c "SSH OK")/${#SERVER_IPS[@]} servers"
echo "- Docker Status: $(test_docker_status 2>&1 | grep -c "Docker ativo")/${#SERVER_IPS[@]} servers"
echo "- External HTTP: $(curl -f -s "http://$DOMAIN/nginx-health" >/dev/null 2>&1 && echo "PASS" || echo "FAIL")"
echo "- External HTTPS: $(curl -f -s "https://$DOMAIN/nginx-health" >/dev/null 2>&1 && echo "PASS" || echo "FAIL")"
echo "- App Health Check: $(curl -f -s "http://$DOMAIN/health" >/dev/null 2>&1 && echo "PASS" || echo "FAIL")"
} > $report_file
info "Relatório salvo em: $report_file"
}
# Função principal
main() {
log "Iniciando testes de conectividade BCards..."
echo ""
test_ssh_connectivity
test_docker_status
test_swarm_status
test_docker_services
test_internal_health
test_dns_resolution
test_external_connectivity
test_load_balancing
test_ssl_certificate
show_performance_metrics
if [[ "$1" == "--report" ]]; then
generate_report
fi
log "Testes de conectividade concluídos!"
}
# Função de ajuda
show_help() {
echo "BCards Connectivity Test Script"
echo ""
echo "Usage: $0 [OPTIONS]"
echo ""
echo "OPTIONS:"
echo " --report Gera relatório detalhado em arquivo"
echo " --help Mostra esta ajuda"
echo ""
echo "Este script testa:"
echo "- Conectividade SSH com os servidores"
echo "- Status do Docker e Docker Swarm"
echo "- Saúde dos serviços em execução"
echo "- Conectividade HTTP/HTTPS externa"
echo "- Funcionamento do load balancing"
echo "- Resolução DNS e certificados SSL"
echo "- Métricas de performance dos servidores"
echo ""
echo "Servers: ${SERVER_IPS[*]}"
echo "Domain: $DOMAIN"
}
# Parse argumentos
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
show_help
exit 0
fi
# Verificar dependências
if ! command -v curl >/dev/null 2>&1; then
error "curl não está instalado"
exit 1
fi
if ! command -v jq >/dev/null 2>&1; then
warn "jq não está instalado - alguns testes serão limitados"
fi
# Executar função principal
main "$@"

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,47 @@
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
namespace BCardsTestApp.Controllers;
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
var serverName = Environment.GetEnvironmentVariable("SERVER_NAME") ?? "Unknown Server";
var serverColor = Environment.GetEnvironmentVariable("SERVER_COLOR") ?? "#007bff";
ViewBag.ServerName = serverName;
ViewBag.ServerColor = serverColor;
ViewBag.Hostname = Environment.MachineName;
ViewBag.Timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss UTC");
_logger.LogInformation("Home page accessed from server {ServerName}", serverName);
return View();
}
public IActionResult About()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
public class ErrorViewModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}

51
test-app/Dockerfile Normal file
View File

@ -0,0 +1,51 @@
# BCards Test App - Multi-stage Docker build com suporte ARM64
FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 8080
# Criar usuário não-root para segurança
RUN addgroup --group appgroup --gid 1001 && \
adduser --uid 1001 --gid 1001 --disabled-password --gecos "" appuser
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG TARGETARCH
WORKDIR /src
# Copiar arquivo de projeto e restaurar dependências
COPY ["BCardsTestApp.csproj", "."]
RUN dotnet restore "BCardsTestApp.csproj"
# Copiar código fonte e compilar
COPY . .
RUN dotnet build "BCardsTestApp.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "BCardsTestApp.csproj" -c Release -o /app/publish /p:UseAppHost=false -a $TARGETARCH
# Imagem final
FROM base AS final
WORKDIR /app
# Copiar aplicação compilada
COPY --from=publish /app/publish .
# Configurar usuário não-root
RUN chown -R appuser:appgroup /app
USER appuser
# Labels para metadados
LABEL maintainer="BCards Team"
LABEL version="1.0"
LABEL description="BCards Test Application for Infrastructure Testing"
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# Configurar variáveis de ambiente padrão
ENV ASPNETCORE_ENVIRONMENT=Production
ENV ASPNETCORE_URLS=http://+:8080
ENV SERVER_NAME="Unknown Server"
ENV SERVER_COLOR="#007bff"
ENTRYPOINT ["dotnet", "BCardsTestApp.dll"]

51
test-app/Program.cs Normal file
View File

@ -0,0 +1,51 @@
using Microsoft.AspNetCore.HttpOverrides;
var builder = WebApplication.CreateBuilder(args);
// Configurar para escutar na porta 8080
builder.WebHost.UseUrls("http://0.0.0.0:8080");
// Configurar serviços
builder.Services.AddControllersWithViews();
builder.Services.AddHealthChecks();
var app = builder.Build();
// Configurar headers para proxy reverso
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
// Health check endpoint
app.MapHealthChecks("/health");
// Endpoint para identificar o servidor
app.MapGet("/server-info", () =>
{
var serverName = Environment.GetEnvironmentVariable("SERVER_NAME") ?? "Unknown";
var hostname = Environment.MachineName;
var timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss UTC");
return Results.Json(new
{
ServerName = serverName,
Hostname = hostname,
Timestamp = timestamp,
Environment = app.Environment.EnvironmentName
});
});
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();

View File

@ -0,0 +1,103 @@
@{
ViewData["Title"] = "About";
}
<div class="row">
<div class="col-12">
<h2><i class="fas fa-info-circle"></i> About BCards Infrastructure</h2>
<hr>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h5>Infrastructure Overview</h5>
</div>
<div class="card-body">
<h6>Architecture Components:</h6>
<ul>
<li><strong>Load Balancer:</strong> NGINX reverse proxy with SSL termination</li>
<li><strong>Application Servers:</strong> 2x OCI instances running Docker containers</li>
<li><strong>Container Orchestration:</strong> Docker Swarm for service management</li>
<li><strong>Domain:</strong> bcards.site with automatic SSL certificates</li>
<li><strong>Registry:</strong> Private container registry at registry.redecarneir.us</li>
</ul>
<h6 class="mt-4">Key Features:</h6>
<ul>
<li>Zero-downtime deployments</li>
<li>Automatic health checks and failover</li>
<li>Centralized logging and monitoring</li>
<li>SSL/TLS encryption</li>
<li>Horizontal scaling capability</li>
</ul>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5>Server Information</h5>
</div>
<div class="card-body">
<p><strong>Current Server:</strong></p>
<div class="alert alert-primary">
@ViewBag.ServerName
</div>
<p><strong>Container Host:</strong></p>
<div class="alert alert-secondary">
@ViewBag.Hostname
</div>
<p><strong>Timestamp:</strong></p>
<div class="alert alert-info">
@ViewBag.Timestamp
</div>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5><i class="fas fa-network-wired"></i> Network Architecture</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h6>Production Servers:</h6>
<ul class="list-group">
<li class="list-group-item d-flex justify-content-between align-items-center">
Server 1 (Manager)
<span class="badge bg-primary rounded-pill">129.153.123.92</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
Server 2 (Worker)
<span class="badge bg-secondary rounded-pill">129.146.116.218</span>
</li>
</ul>
</div>
<div class="col-md-6">
<h6>Service Endpoints:</h6>
<ul class="list-group">
<li class="list-group-item d-flex justify-content-between align-items-center">
Public Domain
<span class="badge bg-success rounded-pill">bcards.site</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
Container Registry
<span class="badge bg-warning rounded-pill">registry.redecarneir.us</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,153 @@
@{
ViewData["Title"] = "Home";
}
<div class="row">
<div class="col-12">
<div class="jumbotron bg-primary text-white p-5 rounded" style="background-color: @ViewBag.ServerColor !important;">
<div class="container-fluid text-center">
<h1 class="display-4">
<i class="fas fa-server"></i>
@ViewBag.ServerName
</h1>
<p class="lead">BCards Infrastructure Test Application</p>
<hr class="my-4 bg-white">
<div class="row">
<div class="col-md-4">
<h5><i class="fas fa-computer"></i> Hostname</h5>
<p class="font-monospace">@ViewBag.Hostname</p>
</div>
<div class="col-md-4">
<h5><i class="fas fa-clock"></i> Timestamp</h5>
<p class="font-monospace">@ViewBag.Timestamp</p>
</div>
<div class="col-md-4">
<h5><i class="fas fa-network-wired"></i> Load Balancer</h5>
<p>Status: <span class="badge bg-success">Active</span></p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5><i class="fas fa-info-circle"></i> Server Information</h5>
</div>
<div class="card-body">
<table class="table table-sm">
<tr>
<td><strong>Server Name:</strong></td>
<td>@ViewBag.ServerName</td>
</tr>
<tr>
<td><strong>Container Host:</strong></td>
<td>@ViewBag.Hostname</td>
</tr>
<tr>
<td><strong>Generated At:</strong></td>
<td>@ViewBag.Timestamp</td>
</tr>
<tr>
<td><strong>Environment:</strong></td>
<td><span class="badge bg-info">Production</span></td>
</tr>
</table>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5><i class="fas fa-chart-line"></i> Quick Tests</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<button class="btn btn-outline-primary" onclick="testServerInfo()">
<i class="fas fa-server"></i> Test Server Info API
</button>
<button class="btn btn-outline-success" onclick="testHealthCheck()">
<i class="fas fa-heart"></i> Test Health Check
</button>
<button class="btn btn-outline-warning" onclick="refreshPage()">
<i class="fas fa-refresh"></i> Refresh Page (Test Load Balancer)
</button>
</div>
<div id="testResults" class="mt-3"></div>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-12">
<div class="alert alert-info">
<h6><i class="fas fa-lightbulb"></i> Infrastructure Test Info</h6>
<p class="mb-0">
Esta aplicação está sendo servida através de um load balancer NGINX configurado para distribuir
requisições entre dois servidores OCI. Cada refresh da página pode mostrar um servidor diferente,
demonstrando o balanceamento de carga em funcionamento.
</p>
</div>
</div>
</div>
@section Scripts {
<script>
async function testServerInfo() {
const resultDiv = document.getElementById('testResults');
try {
const response = await fetch('/server-info');
const data = await response.json();
resultDiv.innerHTML = `
<div class="alert alert-success">
<strong>Server Info API Response:</strong><br>
<pre>${JSON.stringify(data, null, 2)}</pre>
</div>
`;
} catch (error) {
resultDiv.innerHTML = `
<div class="alert alert-danger">
<strong>Error:</strong> ${error.message}
</div>
`;
}
}
async function testHealthCheck() {
const resultDiv = document.getElementById('testResults');
try {
const response = await fetch('/health');
const status = response.ok ? 'Healthy' : 'Unhealthy';
resultDiv.innerHTML = `
<div class="alert alert-${response.ok ? 'success' : 'danger'}">
<strong>Health Check:</strong> ${status} (${response.status})
</div>
`;
} catch (error) {
resultDiv.innerHTML = `
<div class="alert alert-danger">
<strong>Health Check Error:</strong> ${error.message}
</div>
`;
}
}
function refreshPage() {
window.location.reload();
}
// Auto-refresh a cada 30 segundos para demonstrar load balancing
setInterval(() => {
const autoRefresh = document.getElementById('autoRefresh');
if (autoRefresh && autoRefresh.checked) {
refreshPage();
}
}, 30000);
</script>
}

View File

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - BCards Test App</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-dark bg-dark border-bottom box-shadow mb-3">
<div class="container-fluid">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">
<strong>BCards</strong> Test App
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-light" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-light" asp-area="" asp-controller="Home" asp-action="About">About</a>
</li>
</ul>
<div class="navbar-text text-light">
<i class="fas fa-server"></i> @ViewBag.ServerName
</div>
</div>
</div>
</nav>
</header>
<div class="container-fluid">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted bg-light">
<div class="container-fluid text-center py-3">
<small>
BCards Infrastructure Test |
Host: @ViewBag.Hostname |
Generated at: @ViewBag.Timestamp
</small>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://kit.fontawesome.com/your-fontawesome-kit.js" crossorigin="anonymous"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

View File

@ -0,0 +1,2 @@
@using BCardsTestApp
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View File

@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}

View File

@ -0,0 +1,38 @@
version: '3.8'
services:
# Aplicação para teste local
bcards-test-app:
build: .
ports:
- "8080:8080"
environment:
- ASPNETCORE_ENVIRONMENT=Development
- SERVER_NAME=Local Development Server
- SERVER_COLOR=#28a745
networks:
- bcards-local
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# NGINX para teste local do load balancer
nginx-local:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ../nginx/nginx-local.conf:/etc/nginx/nginx.conf:ro
depends_on:
- bcards-test-app
networks:
- bcards-local
restart: unless-stopped
networks:
bcards-local:
driver: bridge

View File

@ -0,0 +1,83 @@
{
"format": 1,
"restore": {
"C:\\vscode\\bcards-infrastructure\\test-app\\BCardsTestApp.csproj": {}
},
"projects": {
"C:\\vscode\\bcards-infrastructure\\test-app\\BCardsTestApp.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "C:\\vscode\\bcards-infrastructure\\test-app\\BCardsTestApp.csproj",
"projectName": "BCardsTestApp",
"projectPath": "C:\\vscode\\bcards-infrastructure\\test-app\\BCardsTestApp.csproj",
"packagesPath": "C:\\Users\\ricar\\.nuget\\packages\\",
"outputPath": "C:\\vscode\\bcards-infrastructure\\test-app\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
],
"configFilePaths": [
"C:\\Users\\ricar\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"net8.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Program Files\\dotnet\\library-packs": {},
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "9.0.200"
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"dependencies": {
"Microsoft.AspNetCore.HealthChecks": {
"target": "Package",
"version": "[1.0.0, )"
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.AspNetCore.App": {
"privateAssets": "none"
},
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.200/PortableRuntimeIdentifierGraph.json"
}
}
}
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\ricar\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.13.0</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="C:\Users\ricar\.nuget\packages\" />
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
</ItemGroup>
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<PkgNewtonsoft_Json Condition=" '$(PkgNewtonsoft_Json)' == '' ">C:\Users\ricar\.nuget\packages\newtonsoft.json\9.0.1</PkgNewtonsoft_Json>
<PkgMicrosoft_CodeAnalysis_Analyzers Condition=" '$(PkgMicrosoft_CodeAnalysis_Analyzers)' == '' ">C:\Users\ricar\.nuget\packages\microsoft.codeanalysis.analyzers\1.1.0</PkgMicrosoft_CodeAnalysis_Analyzers>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />

View File

@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]

View File

@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("BCardsTestApp")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyProductAttribute("BCardsTestApp")]
[assembly: System.Reflection.AssemblyTitleAttribute("BCardsTestApp")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Generated by the MSBuild WriteCodeFragment class.

View File

@ -0,0 +1 @@
789d0a40f649e56edcc5a31f053113bc14cc0f66ea14b7a3531c6f932e11104d

View File

@ -0,0 +1,41 @@
is_global = true
build_property.TargetFramework = net8.0
build_property.TargetPlatformMinVersion =
build_property.UsingMicrosoftNETSdkWeb = true
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = BCardsTestApp
build_property.RootNamespace = BCardsTestApp
build_property.ProjectDir = C:\vscode\bcards-infrastructure\test-app\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.RazorLangVersion = 8.0
build_property.SupportLocalizedComponentNames =
build_property.GenerateRazorMetadataSourceChecksumAttributes =
build_property.MSBuildProjectDirectory = C:\vscode\bcards-infrastructure\test-app
build_property._RazorSourceGeneratorDebug =
build_property.EffectiveAnalysisLevelStyle = 8.0
build_property.EnableCodeStyleSeverity =
[C:/vscode/bcards-infrastructure/test-app/Views/Home/About.cshtml]
build_metadata.AdditionalFiles.TargetPath = Vmlld3NcSG9tZVxBYm91dC5jc2h0bWw=
build_metadata.AdditionalFiles.CssScope =
[C:/vscode/bcards-infrastructure/test-app/Views/Home/Index.cshtml]
build_metadata.AdditionalFiles.TargetPath = Vmlld3NcSG9tZVxJbmRleC5jc2h0bWw=
build_metadata.AdditionalFiles.CssScope =
[C:/vscode/bcards-infrastructure/test-app/Views/Shared/_Layout.cshtml]
build_metadata.AdditionalFiles.TargetPath = Vmlld3NcU2hhcmVkXF9MYXlvdXQuY3NodG1s
build_metadata.AdditionalFiles.CssScope =
[C:/vscode/bcards-infrastructure/test-app/Views/_ViewImports.cshtml]
build_metadata.AdditionalFiles.TargetPath = Vmlld3NcX1ZpZXdJbXBvcnRzLmNzaHRtbA==
build_metadata.AdditionalFiles.CssScope =
[C:/vscode/bcards-infrastructure/test-app/Views/_ViewStart.cshtml]
build_metadata.AdditionalFiles.TargetPath = Vmlld3NcX1ZpZXdTdGFydC5jc2h0bWw=
build_metadata.AdditionalFiles.CssScope =

View File

@ -0,0 +1,17 @@
// <auto-generated/>
global using global::Microsoft.AspNetCore.Builder;
global using global::Microsoft.AspNetCore.Hosting;
global using global::Microsoft.AspNetCore.Http;
global using global::Microsoft.AspNetCore.Routing;
global using global::Microsoft.Extensions.Configuration;
global using global::Microsoft.Extensions.DependencyInjection;
global using global::Microsoft.Extensions.Hosting;
global using global::Microsoft.Extensions.Logging;
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Net.Http.Json;
global using global::System.Threading;
global using global::System.Threading.Tasks;

View File

@ -0,0 +1 @@
d5ac7ab69059af111e9d7125adeb7b174ca570725d4b64a544cca7bd11ac7ca0

View File

@ -0,0 +1,17 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: Microsoft.AspNetCore.Mvc.ApplicationParts.ProvideApplicationPartFactoryAttribute(("Microsoft.AspNetCore.Mvc.ApplicationParts.ConsolidatedAssemblyApplicationPartFact" +
"ory, Microsoft.AspNetCore.Mvc.Razor"))]
// Generated by the MSBuild WriteCodeFragment class.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,162 @@
{
"version": 2,
"dgSpecHash": "znQK0+PCGGU=",
"success": true,
"projectFilePath": "C:\\vscode\\bcards-infrastructure\\test-app\\BCardsTestApp.csproj",
"expectedPackageFiles": [
"C:\\Users\\ricar\\.nuget\\packages\\libuv\\1.9.1\\libuv.1.9.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.aspnetcore.healthchecks\\1.0.0\\microsoft.aspnetcore.healthchecks.1.0.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.aspnetcore.hosting\\1.0.2\\microsoft.aspnetcore.hosting.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.aspnetcore.hosting.abstractions\\1.0.2\\microsoft.aspnetcore.hosting.abstractions.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.aspnetcore.hosting.server.abstractions\\1.0.2\\microsoft.aspnetcore.hosting.server.abstractions.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.aspnetcore.http\\1.0.2\\microsoft.aspnetcore.http.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.aspnetcore.http.abstractions\\1.0.2\\microsoft.aspnetcore.http.abstractions.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.aspnetcore.http.extensions\\1.0.2\\microsoft.aspnetcore.http.extensions.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.aspnetcore.http.features\\1.0.2\\microsoft.aspnetcore.http.features.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.aspnetcore.webutilities\\1.0.2\\microsoft.aspnetcore.webutilities.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.codeanalysis.analyzers\\1.1.0\\microsoft.codeanalysis.analyzers.1.1.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.codeanalysis.common\\1.3.0\\microsoft.codeanalysis.common.1.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.codeanalysis.csharp\\1.3.0\\microsoft.codeanalysis.csharp.1.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.codeanalysis.visualbasic\\1.3.0\\microsoft.codeanalysis.visualbasic.1.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.csharp\\4.0.1\\microsoft.csharp.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.extensions.configuration\\1.0.2\\microsoft.extensions.configuration.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.extensions.configuration.abstractions\\1.0.2\\microsoft.extensions.configuration.abstractions.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.extensions.configuration.environmentvariables\\1.0.2\\microsoft.extensions.configuration.environmentvariables.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.extensions.dependencyinjection\\1.0.2\\microsoft.extensions.dependencyinjection.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.extensions.dependencyinjection.abstractions\\1.0.2\\microsoft.extensions.dependencyinjection.abstractions.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.extensions.fileproviders.abstractions\\1.0.1\\microsoft.extensions.fileproviders.abstractions.1.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.extensions.fileproviders.physical\\1.0.1\\microsoft.extensions.fileproviders.physical.1.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.extensions.filesystemglobbing\\1.0.1\\microsoft.extensions.filesystemglobbing.1.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.extensions.healthchecks\\1.0.0\\microsoft.extensions.healthchecks.1.0.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.extensions.logging\\1.0.2\\microsoft.extensions.logging.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.extensions.logging.abstractions\\1.0.2\\microsoft.extensions.logging.abstractions.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.extensions.objectpool\\1.0.1\\microsoft.extensions.objectpool.1.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.extensions.options\\1.0.2\\microsoft.extensions.options.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.extensions.platformabstractions\\1.0.0\\microsoft.extensions.platformabstractions.1.0.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.extensions.primitives\\1.0.1\\microsoft.extensions.primitives.1.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.net.http.headers\\1.0.2\\microsoft.net.http.headers.1.0.2.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.netcore.app\\1.0.5\\microsoft.netcore.app.1.0.5.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.netcore.dotnethost\\1.0.1\\microsoft.netcore.dotnethost.1.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.netcore.dotnethostpolicy\\1.0.5\\microsoft.netcore.dotnethostpolicy.1.0.5.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.netcore.dotnethostresolver\\1.0.1\\microsoft.netcore.dotnethostresolver.1.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.netcore.jit\\1.0.7\\microsoft.netcore.jit.1.0.7.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.netcore.platforms\\1.1.0\\microsoft.netcore.platforms.1.1.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.netcore.runtime.coreclr\\1.0.7\\microsoft.netcore.runtime.coreclr.1.0.7.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.netcore.targets\\1.1.0\\microsoft.netcore.targets.1.1.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.netcore.windows.apisets\\1.0.1\\microsoft.netcore.windows.apisets.1.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.visualbasic\\10.0.1\\microsoft.visualbasic.10.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.win32.primitives\\4.3.0\\microsoft.win32.primitives.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\microsoft.win32.registry\\4.3.0\\microsoft.win32.registry.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\netstandard.library\\1.6.1\\netstandard.library.1.6.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\newtonsoft.json\\9.0.1\\newtonsoft.json.9.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl\\4.3.0\\runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl\\4.3.0\\runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl\\4.3.0\\runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.native.system\\4.3.0\\runtime.native.system.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.native.system.io.compression\\4.3.0\\runtime.native.system.io.compression.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.native.system.net.http\\4.3.0\\runtime.native.system.net.http.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.native.system.net.security\\4.0.1\\runtime.native.system.net.security.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.native.system.security.cryptography\\4.0.1\\runtime.native.system.security.cryptography.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.native.system.security.cryptography.apple\\4.3.0\\runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.native.system.security.cryptography.openssl\\4.3.0\\runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl\\4.3.0\\runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl\\4.3.0\\runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple\\4.3.0\\runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl\\4.3.0\\runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl\\4.3.0\\runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl\\4.3.0\\runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl\\4.3.0\\runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl\\4.3.0\\runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.appcontext\\4.3.0\\system.appcontext.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.buffers\\4.3.0\\system.buffers.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.collections\\4.3.0\\system.collections.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.collections.concurrent\\4.3.0\\system.collections.concurrent.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.collections.immutable\\1.2.0\\system.collections.immutable.1.2.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.componentmodel\\4.0.1\\system.componentmodel.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.componentmodel.annotations\\4.1.0\\system.componentmodel.annotations.4.1.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.console\\4.3.0\\system.console.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.diagnostics.contracts\\4.0.1\\system.diagnostics.contracts.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.diagnostics.debug\\4.3.0\\system.diagnostics.debug.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.diagnostics.diagnosticsource\\4.3.0\\system.diagnostics.diagnosticsource.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.diagnostics.fileversioninfo\\4.0.0\\system.diagnostics.fileversioninfo.4.0.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.diagnostics.process\\4.3.0\\system.diagnostics.process.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.diagnostics.stacktrace\\4.0.1\\system.diagnostics.stacktrace.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.diagnostics.tools\\4.3.0\\system.diagnostics.tools.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.diagnostics.tracing\\4.3.0\\system.diagnostics.tracing.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.dynamic.runtime\\4.0.11\\system.dynamic.runtime.4.0.11.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.globalization\\4.3.0\\system.globalization.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.globalization.calendars\\4.3.0\\system.globalization.calendars.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.globalization.extensions\\4.3.0\\system.globalization.extensions.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.io\\4.3.0\\system.io.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.io.compression\\4.3.0\\system.io.compression.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.io.compression.zipfile\\4.3.0\\system.io.compression.zipfile.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.io.filesystem\\4.3.0\\system.io.filesystem.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.io.filesystem.primitives\\4.3.0\\system.io.filesystem.primitives.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.io.filesystem.watcher\\4.0.0\\system.io.filesystem.watcher.4.0.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.io.memorymappedfiles\\4.0.0\\system.io.memorymappedfiles.4.0.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.io.unmanagedmemorystream\\4.0.1\\system.io.unmanagedmemorystream.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.linq\\4.3.0\\system.linq.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.linq.expressions\\4.3.0\\system.linq.expressions.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.linq.parallel\\4.0.1\\system.linq.parallel.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.linq.queryable\\4.0.1\\system.linq.queryable.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.net.http\\4.3.0\\system.net.http.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.net.nameresolution\\4.0.0\\system.net.nameresolution.4.0.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.net.primitives\\4.3.0\\system.net.primitives.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.net.requests\\4.0.11\\system.net.requests.4.0.11.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.net.security\\4.0.1\\system.net.security.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.net.sockets\\4.3.0\\system.net.sockets.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.net.webheadercollection\\4.0.1\\system.net.webheadercollection.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.net.websockets\\4.0.0\\system.net.websockets.4.0.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.numerics.vectors\\4.1.1\\system.numerics.vectors.4.1.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.objectmodel\\4.3.0\\system.objectmodel.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.reflection\\4.3.0\\system.reflection.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.reflection.dispatchproxy\\4.0.1\\system.reflection.dispatchproxy.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.reflection.emit\\4.3.0\\system.reflection.emit.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.reflection.emit.ilgeneration\\4.3.0\\system.reflection.emit.ilgeneration.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.reflection.emit.lightweight\\4.3.0\\system.reflection.emit.lightweight.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.reflection.extensions\\4.3.0\\system.reflection.extensions.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.reflection.metadata\\1.3.0\\system.reflection.metadata.1.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.reflection.primitives\\4.3.0\\system.reflection.primitives.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.reflection.typeextensions\\4.3.0\\system.reflection.typeextensions.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.resources.reader\\4.0.0\\system.resources.reader.4.0.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.resources.resourcemanager\\4.3.0\\system.resources.resourcemanager.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.runtime\\4.3.0\\system.runtime.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.runtime.extensions\\4.3.0\\system.runtime.extensions.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.runtime.handles\\4.3.0\\system.runtime.handles.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.runtime.interopservices\\4.3.0\\system.runtime.interopservices.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.runtime.interopservices.runtimeinformation\\4.3.0\\system.runtime.interopservices.runtimeinformation.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.runtime.loader\\4.0.0\\system.runtime.loader.4.0.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.runtime.numerics\\4.3.0\\system.runtime.numerics.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.runtime.serialization.primitives\\4.1.1\\system.runtime.serialization.primitives.4.1.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.security.claims\\4.0.1\\system.security.claims.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.security.cryptography.algorithms\\4.3.0\\system.security.cryptography.algorithms.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.security.cryptography.cng\\4.3.0\\system.security.cryptography.cng.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.security.cryptography.csp\\4.3.0\\system.security.cryptography.csp.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.security.cryptography.encoding\\4.3.0\\system.security.cryptography.encoding.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.security.cryptography.openssl\\4.3.0\\system.security.cryptography.openssl.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.security.cryptography.primitives\\4.3.0\\system.security.cryptography.primitives.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.security.cryptography.x509certificates\\4.3.0\\system.security.cryptography.x509certificates.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.security.principal\\4.0.1\\system.security.principal.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.security.principal.windows\\4.0.0\\system.security.principal.windows.4.0.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.text.encoding\\4.3.0\\system.text.encoding.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.text.encoding.codepages\\4.0.1\\system.text.encoding.codepages.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.text.encoding.extensions\\4.3.0\\system.text.encoding.extensions.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.text.encodings.web\\4.0.0\\system.text.encodings.web.4.0.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.text.regularexpressions\\4.3.0\\system.text.regularexpressions.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.threading\\4.3.0\\system.threading.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.threading.overlapped\\4.0.1\\system.threading.overlapped.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.threading.tasks\\4.3.0\\system.threading.tasks.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.threading.tasks.dataflow\\4.6.0\\system.threading.tasks.dataflow.4.6.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.threading.tasks.extensions\\4.3.0\\system.threading.tasks.extensions.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.threading.tasks.parallel\\4.3.0\\system.threading.tasks.parallel.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.threading.thread\\4.3.0\\system.threading.thread.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.threading.threadpool\\4.3.0\\system.threading.threadpool.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.threading.timer\\4.3.0\\system.threading.timer.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.xml.readerwriter\\4.3.0\\system.xml.readerwriter.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.xml.xdocument\\4.3.0\\system.xml.xdocument.4.3.0.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.xml.xmldocument\\4.0.1\\system.xml.xmldocument.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.xml.xpath\\4.0.1\\system.xml.xpath.4.0.1.nupkg.sha512",
"C:\\Users\\ricar\\.nuget\\packages\\system.xml.xpath.xdocument\\4.0.1\\system.xml.xpath.xdocument.4.0.1.nupkg.sha512"
],
"logs": []
}

View File

@ -0,0 +1,135 @@
/* BCards Test App Styles */
html {
font-size: 14px;
}
@media (min-width: 768px) {
html {
font-size: 16px;
}
}
html {
position: relative;
min-height: 100%;
}
body {
margin-bottom: 60px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
white-space: nowrap;
line-height: 60px;
}
.jumbotron {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.card {
border: none;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: all 0.3s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
.btn {
border-radius: 6px;
font-weight: 500;
transition: all 0.3s ease;
}
.btn:hover {
transform: translateY(-1px);
}
.alert {
border-radius: 8px;
border: none;
}
.badge {
font-size: 0.8em;
}
pre {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 1rem;
font-size: 0.875rem;
max-height: 300px;
overflow-y: auto;
}
.navbar-brand {
font-weight: 700;
}
.list-group-item {
border: none;
border-bottom: 1px solid #e9ecef;
}
.list-group-item:last-child {
border-bottom: none;
}
/* Loading animation */
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid rgba(255,255,255,.3);
border-radius: 50%;
border-top-color: #fff;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Server status indicators */
.server-status {
width: 12px;
height: 12px;
border-radius: 50%;
display: inline-block;
margin-right: 8px;
}
.server-status.online {
background-color: #28a745;
box-shadow: 0 0 0 2px rgba(40, 167, 69, 0.3);
}
.server-status.offline {
background-color: #dc3545;
box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.3);
}
/* Dark theme support */
@media (prefers-color-scheme: dark) {
.card {
background-color: #2d3748;
color: #e2e8f0;
}
.alert {
background-color: #4a5568;
color: #e2e8f0;
}
}