Adiciona estrutura inicial do projeto NestJS com Docker, incluindo Dockerfile, docker-compose, scripts de deploy e configuração do Nginx.

This commit is contained in:
2026-04-17 23:19:27 -05:00
parent 2e76495db8
commit 80d25b2db7
8 changed files with 437 additions and 1 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
# Nunca sobe projetos NestJS — ficam só no VPS
projects/
node_modules/
*.log
.env

32
Dockerfile Normal file
View File

@@ -0,0 +1,32 @@
FROM node:20-alpine
# Install system deps
RUN apk add --no-cache \
nginx \
git \
bash \
curl \
openssl
# Install PM2 globally
RUN npm install -g pm2
# Create directories
RUN mkdir -p /home/deploy/projects \
/home/deploy/scripts \
/etc/nginx/sites-dynamic \
/var/log/nginx \
/run/nginx
# Copy scripts
COPY scripts/ /home/deploy/scripts/
RUN chmod +x /home/deploy/scripts/*.sh
# Copy nginx base config
COPY nginx/nginx.conf /etc/nginx/nginx.conf
WORKDIR /home/deploy
EXPOSE 80
CMD ["/home/deploy/scripts/start.sh"]

135
README.md
View File

@@ -1,2 +1,135 @@
# docker-nest-js # NestJS Multi-Projeto Stack — Portainer
## Estrutura no Host (VPS)
```
/home/deploy/
├── nest-stack/ ← este repositório
│ ├── Dockerfile
│ ├── docker-compose.yml
│ ├── nginx/nginx.conf
│ ├── scripts/
│ │ ├── start.sh ← entrypoint do container
│ │ └── deploy.sh ← deploy dentro do container
│ └── host-deploy.sh ← atalho para rodar do host
└── projects/ ← SEUS PROJETOS (volume montado)
├── projeto1/ ← git clone aqui
└── projeto2/
```
---
## Setup inicial
### 1. No servidor (VPS), clone este repo
```bash
git clone <este-repo> /home/deploy/nest-stack
mkdir -p /home/deploy/projects
chmod +x /home/deploy/nest-stack/host-deploy.sh
```
### 2. No Portainer → Stacks → Add Stack
- **Name:** `nestjs-stack`
- **Build method:** Repository (aponta para este repo)
— OU —
- **Build method:** Upload → sobe o `docker-compose.yml`
### 3. Clique em **Deploy the stack**
O container vai:
- Buildar a imagem (Nginx + Node + PM2)
- Montar `/home/deploy/projects` como volume
- Subir projetos que já tiverem build em `dist/`
---
## Adicionando um novo projeto
```bash
# No HOST (fora do container)
cd /home/deploy/projects
git clone https://github.com/seu-usuario/meu-projeto.git projeto1
# Roda o deploy
./home/deploy/nest-stack/host-deploy.sh projeto1
# Pronto! Acessível em:
# http://nest.juancjc.com.br/projeto1
```
---
## Atualizando um projeto existente
```bash
# Atualiza o código
cd /home/deploy/projects/projeto1
git pull
# Redeploya
/home/deploy/nest-stack/host-deploy.sh projeto1
```
---
## Reset do container (via Portainer)
1. Portainer → Containers → `nestjs-stack`**Restart** ou **Reset**
2. O container sobe e **automaticamente relança todos os projetos** que tiverem `dist/main.js`
3. Os arquivos dos projetos continuam intactos (estão no volume do host)
---
## Comandos úteis
```bash
# Ver logs do container
docker logs nestjs-stack -f
# Ver processos PM2 rodando
docker exec nestjs-stack pm2 list
# Ver logs de um projeto específico
docker exec nestjs-stack pm2 logs projeto1
# Entrar no container
docker exec -it nestjs-stack bash
# Ver status do nginx
docker exec nestjs-stack nginx -t
```
---
## Regras de porta
| Projeto | Porta interna |
|---------------|---------------|
| projeto1 | 3001 |
| projeto2 | 3002 |
| projeto3 | 3003 |
| projetoN | 300N |
A porta é determinada pela **ordem alfabética das pastas** em `/home/deploy/projects`.
---
## Seu projeto NestJS não precisa de nenhuma config especial
O Nginx remove o prefixo `/projeto1` antes de repassar ao NestJS.
Seu app recebe as rotas normalmente como `/`.
**Único requisito:** seu `main.ts` deve aceitar a porta via variável de ambiente ou argumento:
```typescript
// main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const port = process.argv.find((_, i, a) => a[i-1] === '--port') || process.env.PORT || 3000;
await app.listen(port);
}
bootstrap();
```

25
docker-compose.yml Normal file
View File

@@ -0,0 +1,25 @@
version: "3.8"
services:
nest-stack:
build:
context: .
dockerfile: Dockerfile
container_name: nestjs-stack
restart: unless-stopped
ports:
- "2442:80"
volumes:
# Projetos ficam no host — persistem no reset do container
- /home/deploy/projects:/home/deploy/projects
# Scripts acessíveis para executar deploy de fora do container
- ./scripts:/home/deploy/scripts
environment:
- PROJECTS_DIR=/home/deploy/projects
- BASE_PORT=3000
- DOMAIN=nest.juancjc.com.br
labels:
- "com.neststack.managed=true"
# Nota: sem volume nomeado para projects — pasta no host é intencional
# Isso permite git clone direto no host sem entrar no container

14
host-deploy.sh Normal file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
# host-deploy.sh — rode isso NO HOST para deployar um projeto
# Uso: ./host-deploy.sh meu-projeto
# Ou: ./host-deploy.sh (deploya todos)
TARGET="${1:-}"
if [ -n "$TARGET" ]; then
echo ">>> Deployando $TARGET via container..."
docker exec nestjs-stack /home/deploy/scripts/deploy.sh "$TARGET"
else
echo ">>> Deployando todos os projetos via container..."
docker exec nestjs-stack /home/deploy/scripts/deploy.sh
fi

36
nginx/nginx.conf Normal file
View File

@@ -0,0 +1,36 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
client_max_body_size 50M;
server {
listen 80;
server_name _;
# Rota raiz — lista projetos disponíveis
location = / {
return 200 'NestJS Stack use /nome-do-projeto para acessar seus projetos';
add_header Content-Type text/plain;
}
# Rotas dinâmicas dos projetos (geradas pelo gen-nginx.sh)
include /etc/nginx/sites-dynamic/*.conf;
}
}

115
scripts/deploy.sh Normal file
View File

@@ -0,0 +1,115 @@
#!/bin/bash
# deploy.sh — deploya um projeto ou todos
# Uso dentro do container:
# /home/deploy/scripts/deploy.sh → deploya todos
# /home/deploy/scripts/deploy.sh meu-projeto → deploya só um
PROJECTS_DIR="${PROJECTS_DIR:-/home/deploy/projects}"
BASE_PORT="${BASE_PORT:-3000}"
TARGET="${1:-}"
deploy_project() {
local project="$1"
local port="$2"
local project_dir="$PROJECTS_DIR/$project"
echo ""
echo ">>> Deployando: $project → porta $port"
if [ ! -d "$project_dir" ]; then
echo "✗ Pasta $project_dir não encontrada"
return 1
fi
cd "$project_dir"
# Instala dependências
echo " → npm install..."
npm install --prefer-offline 2>&1 | tail -3
# Build
echo " → npm run build..."
npm run build
if [ $? -ne 0 ]; then
echo " ✗ Build falhou para $project"
return 1
fi
# Salva a porta usada
echo "$port" > "$project_dir/.port"
# PM2: reinicia se já existe, senão cria
if pm2 describe "$project" > /dev/null 2>&1; then
echo " → Reiniciando processo PM2..."
pm2 restart "$project"
else
echo " → Criando processo PM2..."
pm2 start dist/main.js \
--name "$project" \
--env production \
-- --port "$port"
fi
# Gera/atualiza config nginx
cat > "/etc/nginx/sites-dynamic/${project}.conf" <<EOF
location /${project}/ {
proxy_pass http://localhost:${port}/;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
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_cache_bypass \$http_upgrade;
}
EOF
echo "$project deployado em /$project"
}
# -------------------------------------------
# Lógica principal
# -------------------------------------------
if [ -n "$TARGET" ]; then
# Deploya projeto específico
# Descobre qual porta estava reservada ou pega a próxima livre
port_index=1
for d in "$PROJECTS_DIR"/*/; do
[ -d "$d" ] || continue
name=$(basename "$d")
if [ "$name" = "$TARGET" ]; then
break
fi
port_index=$((port_index + 1))
done
port=$((BASE_PORT + port_index))
deploy_project "$TARGET" "$port"
else
# Deploya todos os projetos
port_index=1
for project_dir in "$PROJECTS_DIR"/*/; do
[ -d "$project_dir" ] || continue
project=$(basename "$project_dir")
port=$((BASE_PORT + port_index))
deploy_project "$project" "$port"
port_index=$((port_index + 1))
done
fi
# Recarrega nginx
echo ""
echo "→ Recarregando Nginx..."
nginx -s reload
pm2 save
echo ""
echo "================================================"
echo " Deploy concluído!"
for project_dir in "$PROJECTS_DIR"/*/; do
[ -d "$project_dir" ] || continue
project=$(basename "$project_dir")
port=$(cat "$project_dir/.port" 2>/dev/null || echo "?")
echo " ✓ /$project → localhost:$port"
done
echo "================================================"

76
scripts/start.sh Normal file
View File

@@ -0,0 +1,76 @@
#!/bin/bash
# start.sh — entrypoint do container
# Roda ao iniciar/resetar o container
PROJECTS_DIR="${PROJECTS_DIR:-/home/deploy/projects}"
BASE_PORT="${BASE_PORT:-3000}"
echo "================================================"
echo " NestJS Stack — Iniciando..."
echo "================================================"
# Garante que a pasta de projetos existe
mkdir -p "$PROJECTS_DIR"
mkdir -p /etc/nginx/sites-dynamic
# Limpa configs antigas do nginx (container pode ter sido resetado)
rm -f /etc/nginx/sites-dynamic/*.conf
# Inicia nginx
echo "→ Iniciando Nginx..."
nginx -t && nginx
echo "✓ Nginx rodando"
# Se houver projetos com build existente, sobe eles via PM2
echo "→ Verificando projetos existentes..."
port_index=1
for project_dir in "$PROJECTS_DIR"/*/; do
[ -d "$project_dir" ] || continue
project=$(basename "$project_dir")
port=$((BASE_PORT + port_index))
# Só sobe se já tiver build
if [ -f "$project_dir/dist/main.js" ]; then
echo " → Subindo $project na porta $port"
pm2 start "$project_dir/dist/main.js" \
--name "$project" \
--env production \
-- --port "$port" 2>/dev/null
# Gera config nginx para esse projeto
cat > "/etc/nginx/sites-dynamic/${project}.conf" <<EOF
location /${project}/ {
proxy_pass http://localhost:${port}/;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
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_cache_bypass \$http_upgrade;
}
EOF
echo "$port" > "$project_dir/.port"
else
echo "$project sem build — rode: docker exec nestjs-stack /home/deploy/scripts/deploy.sh $project"
fi
port_index=$((port_index + 1))
done
# Recarrega nginx com as configs dos projetos
nginx -s reload 2>/dev/null || true
echo "✓ Nginx atualizado com rotas dos projetos"
pm2 save 2>/dev/null || true
echo ""
echo "================================================"
echo " Stack pronta!"
echo " Acesse: http://\${DOMAIN:-localhost}/<projeto>"
echo "================================================"
# Mantém o container vivo e faz log do PM2
pm2 logs --raw