Atualiza scripts e configurações do projeto para melhorar o gerenciamento de projetos e a estrutura do repositório

This commit is contained in:
2026-04-18 14:45:51 -05:00
parent 9cc146348f
commit 163b89aca5
11 changed files with 304 additions and 93 deletions

View File

@@ -18,7 +18,7 @@ RUN mkdir -p /home/deploy/projects \
/var/log/nginx \ /var/log/nginx \
/run/nginx /run/nginx
# Copy scripts # Copy scripts internos do container
COPY scripts/ /home/deploy/scripts/ COPY scripts/ /home/deploy/scripts/
RUN chmod +x /home/deploy/scripts/*.sh RUN chmod +x /home/deploy/scripts/*.sh

170
README.md
View File

@@ -1,134 +1,158 @@
# NestJS Multi-Projeto Stack — Portainer # NestJS Multi-Projeto Stack
## Estrutura no Host (VPS) ## Estrutura do repositório
```
nest-stack/
├── Dockerfile
├── docker-compose.yml
├── .gitignore
├── nginx/
│ └── nginx.conf ← config base do nginx
├── scripts/ ← rodam DENTRO do container
│ ├── start.sh ← entrypoint do container
│ └── deploy.sh ← build + PM2 + nginx
├── host-deploy.sh ← deploya um projeto (rode no host)
├── host-remove.sh ← remove um projeto (rode no host)
└── host-status.sh ← status dos projetos (rode no host)
```
## Estrutura no host (VPS)
``` ```
/home/deploy/ /home/deploy/
├── nest-stack/ ← este repositório ├── nest-stack/ ← este repositório
│ ├── Dockerfile ├── projects/ ← seus projetos NestJS (volume)
│ ├── docker-compose.yml │ ├── projeto1/
── nginx/nginx.conf ── projeto2/
│ ├── scripts/ ├── nginx-dynamic/ ← configs nginx persistidas (volume)
│ │ ├── start.sh ← entrypoint do container └── pm2/ ← estado do PM2 persistido (volume)
│ │ └── 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 ## Setup inicial no VPS
### 1. No servidor (VPS), clone este repo
```bash ```bash
# 1. Clone este repositório
git clone <este-repo> /home/deploy/nest-stack git clone <este-repo> /home/deploy/nest-stack
# 2. Crie as pastas necessárias
mkdir -p /home/deploy/projects mkdir -p /home/deploy/projects
chmod +x /home/deploy/nest-stack/host-deploy.sh mkdir -p /home/deploy/nginx-dynamic
mkdir -p /home/deploy/pm2
# 3. Dê permissão aos scripts
chmod +x /home/deploy/nest-stack/host-*.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 ## Deploy via Portainer
1. **Stacks → Add Stack**
2. **Build method → Repository** → cole a URL deste repo
3. **Deploy the stack**
O container vai buildar e subir automaticamente todos os projetos que já tiverem `dist/main.js`.
---
## Comandos do dia a dia
### Adicionar um projeto
```bash ```bash
# No HOST (fora do container)
cd /home/deploy/projects cd /home/deploy/projects
git clone https://github.com/seu-usuario/meu-projeto.git projeto1 git clone https://github.com/seu-usuario/projeto1.git projeto1
# Roda o deploy # Coloca o .env do projeto
./home/deploy/nest-stack/host-deploy.sh projeto1 nano /home/deploy/projects/projeto1/.env
# Pronto! Acessível em: # Deploya
# 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 /home/deploy/nest-stack/host-deploy.sh projeto1
``` ```
### Atualizar um projeto
```bash
cd /home/deploy/projects/projeto1
git pull
/home/deploy/nest-stack/host-deploy.sh projeto1
```
### Ver status de todos os projetos
```bash
/home/deploy/nest-stack/host-status.sh
```
### Remover um projeto
```bash
# Remove tudo (PM2 + nginx + arquivos)
/home/deploy/nest-stack/host-remove.sh projeto1
# Remove do PM2/nginx mas MANTÉM os arquivos
/home/deploy/nest-stack/host-remove.sh projeto1 --keep-files
```
--- ---
## Reset do container (via Portainer) ## Reiniciar o container (seguro!)
1. Portainer → Containers → `nestjs-stack`**Restart** ou **Reset** Pode reiniciar pelo Portainer sem medo — nada é perdido:
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) - ✅ Arquivos dos projetos `/home/deploy/projects` (volume)
- ✅ Configs Nginx → `/home/deploy/nginx-dynamic` (volume)
- ✅ Estado PM2 → `/home/deploy/pm2` (volume)
-`.env` de cada projeto → nunca apagado
--- ---
## Comandos úteis ## Comandos úteis
```bash ```bash
# Ver logs do container # Logs do container
docker logs nestjs-stack -f docker logs nestjs-stack -f
# Ver processos PM2 rodando # Processos PM2
docker exec nestjs-stack pm2 list docker exec nestjs-stack pm2 list
# Ver logs de um projeto específico # Logs de um projeto
docker exec nestjs-stack pm2 logs projeto1 docker exec nestjs-stack pm2 logs projeto1
# Entrar no container # Entrar no container
docker exec -it nestjs-stack bash docker exec -it nestjs-stack bash
# Ver status do nginx # Ver config completa do nginx
docker exec nestjs-stack nginx -T
# Testar nginx
docker exec nestjs-stack nginx -t docker exec nestjs-stack nginx -t
``` ```
--- ---
## Regras de porta ## Portas internas
| Projeto | Porta interna | | Projeto | Porta |
|---------------|---------------| |------------|--------|
| projeto1 | 3001 | | projeto1 | 3001 |
| projeto2 | 3002 | | projeto2 | 3002 |
| projeto3 | 3003 | | projeto3 | 3003 |
| projetoN | 300N |
A porta é determinada pela **ordem alfabética das pastas** em `/home/deploy/projects`. Ordem alfabética das pastas em `/home/deploy/projects`.
--- ---
## Seu projeto NestJS não precisa de nenhuma config especial ## Requisito no seu projeto NestJS
O Nginx remove o prefixo `/projeto1` antes de repassar ao NestJS. O `main.ts` deve aceitar porta via argumento ou variável de ambiente:
Seu app recebe as rotas normalmente como `/`.
**Único requisito:** seu `main.ts` deve aceitar a porta via variável de ambiente ou argumento:
```typescript ```typescript
// main.ts
async function bootstrap() { async function bootstrap() {
const app = await NestFactory.create(AppModule); const app = await NestFactory.create(AppModule);
const port = process.argv.find((_, i, a) => a[i-1] === '--port') || process.env.PORT || 3000; const port =
process.argv.find((_, i, a) => a[i - 1] === '--port') ||
process.env.PORT ||
3000;
await app.listen(port); await app.listen(port);
} }
bootstrap(); bootstrap();

View File

@@ -11,8 +11,8 @@ services:
- "3451:80" - "3451:80"
volumes: volumes:
- /home/deploy/projects:/home/deploy/projects - /home/deploy/projects:/home/deploy/projects
- /home/deploy/nginx-dynamic:/etc/nginx/sites-dynamic # ← persiste configs nginx - /home/deploy/nginx-dynamic:/etc/nginx/sites-dynamic
- /home/deploy/pm2:/root/.pm2 # ← persiste estado do PM2 - /home/deploy/pm2:/root/.pm2
environment: environment:
- PROJECTS_DIR=/home/deploy/projects - PROJECTS_DIR=/home/deploy/projects
- BASE_PORT=3000 - BASE_PORT=3000

View File

@@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# host-deploy.sh — rode isso NO HOST para deployar um projeto # host-deploy.sh — rode NO HOST para deployar um projeto
# Uso: ./host-deploy.sh meu-projeto # Uso: ./host-deploy.sh meu-projeto
# Ou: ./host-deploy.sh (deploya todos) # Ou: ./host-deploy.sh (deploya todos)

50
host-remove.sh Normal file
View File

@@ -0,0 +1,50 @@
#!/bin/bash
# host-remove.sh — remove um projeto completamente
# Uso: ./host-remove.sh meu-projeto
# Uso: ./host-remove.sh meu-projeto --keep-files (mantém os arquivos no host)
TARGET="${1:-}"
KEEP_FILES="${2:-}"
PROJECTS_DIR="/home/deploy/projects"
if [ -z "$TARGET" ]; then
echo "❌ Informe o nome do projeto."
echo " Uso: ./host-remove.sh meu-projeto"
echo " Uso: ./host-remove.sh meu-projeto --keep-files"
exit 1
fi
echo "================================================"
echo " Removendo projeto: $TARGET"
echo "================================================"
echo "→ Removendo do PM2..."
docker exec nestjs-stack pm2 delete "$TARGET" 2>/dev/null && echo " ✓ PM2 removido" || echo " ⚠ Não estava no PM2"
echo "→ Removendo config do Nginx..."
docker exec nestjs-stack rm -f "/etc/nginx/sites-dynamic/${TARGET}.conf" && echo " ✓ Config nginx removida"
echo "→ Recarregando Nginx..."
docker exec nestjs-stack nginx -s reload && echo " ✓ Nginx recarregado"
docker exec nestjs-stack pm2 save
if [ "$KEEP_FILES" = "--keep-files" ]; then
echo " → Arquivos mantidos em $PROJECTS_DIR/$TARGET"
else
if [ -d "$PROJECTS_DIR/$TARGET" ]; then
echo "→ Removendo arquivos do projeto..."
rm -rf "$PROJECTS_DIR/$TARGET"
echo " ✓ Pasta $PROJECTS_DIR/$TARGET removida"
else
echo " ⚠ Pasta $PROJECTS_DIR/$TARGET não encontrada"
fi
fi
echo ""
echo "================================================"
echo " ✓ Projeto $TARGET removido!"
if [ "$KEEP_FILES" = "--keep-files" ]; then
echo " Arquivos mantidos em $PROJECTS_DIR/$TARGET"
fi
echo "================================================"

65
host-status.sh Normal file
View File

@@ -0,0 +1,65 @@
#!/bin/bash
# host-status.sh — exibe status de todos os projetos na stack
PROJECTS_DIR="/home/deploy/projects"
DOMAIN="${DOMAIN:-nest.juancjc.com.br}"
echo ""
echo "================================================"
echo " NestJS Stack — Status dos Projetos"
echo "================================================"
if [ ! -d "$PROJECTS_DIR" ]; then
echo " ✗ Pasta $PROJECTS_DIR não encontrada"
exit 1
fi
PM2_LIST=$(docker exec nestjs-stack pm2 jlist 2>/dev/null)
found=0
for project_dir in "$PROJECTS_DIR"/*/; do
[ -d "$project_dir" ] || continue
project=$(basename "$project_dir")
port=$(cat "$project_dir/.port" 2>/dev/null || echo "?")
found=$((found + 1))
if [ ! -f "$project_dir/dist/main.js" ]; then
echo "$project → sem build (rode: ./host-deploy.sh $project)"
continue
fi
status=$(echo "$PM2_LIST" | grep -o "\"name\":\"$project\".*?\"status\":\"[^\"]*\"" 2>/dev/null | grep -o "\"status\":\"[^\"]*\"" | cut -d'"' -f4)
if [ -z "$status" ]; then
status="não iniciado"
icon="⚠"
elif [ "$status" = "online" ]; then
icon="✓"
else
icon="✗"
fi
if [ -f "/home/deploy/nginx-dynamic/${project}.conf" ]; then
nginx_status="✓"
else
nginx_status="✗"
fi
echo " $icon $project"
echo " URL: http://$DOMAIN/$project"
echo " Porta: $port"
echo " PM2: $status"
echo " Nginx: $nginx_status"
echo ""
done
if [ "$found" -eq 0 ]; then
echo " Nenhum projeto encontrado em $PROJECTS_DIR"
echo " Clone seus projetos: git clone <repo> $PROJECTS_DIR/<nome>"
fi
echo "================================================"
echo " Container: $(docker inspect -f '{{.State.Status}}' nestjs-stack 2>/dev/null || echo 'não encontrado')"
echo "================================================"
echo ""

View File

@@ -30,7 +30,7 @@ http {
add_header Content-Type text/plain; add_header Content-Type text/plain;
} }
# Rotas dinâmicas dos projetos (geradas pelo gen-nginx.sh) # Rotas dinâmicas dos projetos
include /etc/nginx/sites-dynamic/*.conf; include /etc/nginx/sites-dynamic/*.conf;
} }
} }

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# deploy.sh — deploya um projeto ou todos # deploy.sh — deploya um projeto ou todos (roda DENTRO do container)
# Uso dentro do container: # Uso:
# /home/deploy/scripts/deploy.sh → deploya todos # /home/deploy/scripts/deploy.sh → deploya todos
# /home/deploy/scripts/deploy.sh meu-projeto → deploya só um # /home/deploy/scripts/deploy.sh meu-projeto → deploya só um
@@ -23,6 +23,11 @@ deploy_project() {
cd "$project_dir" cd "$project_dir"
# Mantém .env existente
if [ -f "$project_dir/.env" ]; then
echo " → .env encontrado, mantendo..."
fi
# Instala dependências # Instala dependências
echo " → npm install..." echo " → npm install..."
npm install --prefer-offline 2>&1 | tail -3 npm install --prefer-offline 2>&1 | tail -3
@@ -72,8 +77,6 @@ EOF
# ------------------------------------------- # -------------------------------------------
if [ -n "$TARGET" ]; then if [ -n "$TARGET" ]; then
# Deploya projeto específico
# Descobre qual porta estava reservada ou pega a próxima livre
port_index=1 port_index=1
for d in "$PROJECTS_DIR"/*/; do for d in "$PROJECTS_DIR"/*/; do
[ -d "$d" ] || continue [ -d "$d" ] || continue
@@ -86,7 +89,6 @@ if [ -n "$TARGET" ]; then
port=$((BASE_PORT + port_index)) port=$((BASE_PORT + port_index))
deploy_project "$TARGET" "$port" deploy_project "$TARGET" "$port"
else else
# Deploya todos os projetos
port_index=1 port_index=1
for project_dir in "$PROJECTS_DIR"/*/; do for project_dir in "$PROJECTS_DIR"/*/; do
[ -d "$project_dir" ] || continue [ -d "$project_dir" ] || continue
@@ -97,7 +99,6 @@ else
done done
fi fi
# Recarrega nginx
echo "" echo ""
echo "→ Recarregando Nginx..." echo "→ Recarregando Nginx..."
nginx -s reload nginx -s reload

14
scripts/hot-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

55
scripts/hot-remove.sh Normal file
View File

@@ -0,0 +1,55 @@
#!/bin/bash
# host-remove.sh — remove um projeto completamente
# Uso: ./host-remove.sh meu-projeto
# Uso: ./host-remove.sh meu-projeto --keep-files (mantém os arquivos no host)
TARGET="${1:-}"
KEEP_FILES="${2:-}"
PROJECTS_DIR="/home/deploy/projects"
if [ -z "$TARGET" ]; then
echo "❌ Informe o nome do projeto."
echo " Uso: ./host-remove.sh meu-projeto"
echo " Uso: ./host-remove.sh meu-projeto --keep-files"
exit 1
fi
echo "================================================"
echo " Removendo projeto: $TARGET"
echo "================================================"
# 1. Para e remove do PM2
echo "→ Removendo do PM2..."
docker exec nestjs-stack pm2 delete "$TARGET" 2>/dev/null && echo " ✓ PM2 removido" || echo " ⚠ Não estava no PM2"
# 2. Remove config do nginx
echo "→ Removendo config do Nginx..."
docker exec nestjs-stack rm -f "/etc/nginx/sites-dynamic/${TARGET}.conf" && echo " ✓ Config nginx removida"
# 3. Recarrega nginx
echo "→ Recarregando Nginx..."
docker exec nestjs-stack nginx -s reload && echo " ✓ Nginx recarregado"
# 4. Salva estado do PM2
docker exec nestjs-stack pm2 save
# 5. Remove arquivos do host (opcional)
if [ "$KEEP_FILES" = "--keep-files" ]; then
echo " → Arquivos mantidos em $PROJECTS_DIR/$TARGET"
else
if [ -d "$PROJECTS_DIR/$TARGET" ]; then
echo "→ Removendo arquivos do projeto..."
rm -rf "$PROJECTS_DIR/$TARGET"
echo " ✓ Pasta $PROJECTS_DIR/$TARGET removida"
else
echo " ⚠ Pasta $PROJECTS_DIR/$TARGET não encontrada"
fi
fi
echo ""
echo "================================================"
echo " ✓ Projeto $TARGET removido com sucesso!"
if [ "$KEEP_FILES" = "--keep-files" ]; then
echo " Arquivos mantidos em $PROJECTS_DIR/$TARGET"
fi
echo "================================================"

View File

@@ -1,4 +1,7 @@
#!/bin/bash #!/bin/bash
# start.sh — entrypoint do container
# NÃO apaga projetos nem configs existentes — só sobe o que falta
PROJECTS_DIR="${PROJECTS_DIR:-/home/deploy/projects}" PROJECTS_DIR="${PROJECTS_DIR:-/home/deploy/projects}"
BASE_PORT="${BASE_PORT:-3000}" BASE_PORT="${BASE_PORT:-3000}"
@@ -25,12 +28,11 @@ for project_dir in "$PROJECTS_DIR"/*/; do
if [ -f "$project_dir/dist/main.js" ]; then if [ -f "$project_dir/dist/main.js" ]; then
echo " → Subindo $project na porta $port" echo " → Subindo $project na porta $port"
# Salva porta
echo "$port" > "$project_dir/.port" echo "$port" > "$project_dir/.port"
# Recria config nginx SE não existir # Recria config nginx SOMENTE se não existir
if [ ! -f "/etc/nginx/sites-dynamic/${project}.conf" ]; then if [ ! -f "/etc/nginx/sites-dynamic/${project}.conf" ]; then
echo "Recriando config nginx para $project" echo "Criando config nginx para $project"
cat > "/etc/nginx/sites-dynamic/${project}.conf" <<EOF cat > "/etc/nginx/sites-dynamic/${project}.conf" <<EOF
location /${project}/ { location /${project}/ {
proxy_pass http://localhost:${port}/; proxy_pass http://localhost:${port}/;
@@ -45,14 +47,14 @@ location /${project}/ {
EOF EOF
fi fi
# Sobe no PM2 se não estiver rodando # Sobe no PM2 se não estiver rodando, senão reinicia
if ! pm2 describe "$project" > /dev/null 2>&1; then if pm2 describe "$project" > /dev/null 2>&1; then
pm2 restart "$project"
else
pm2 start "$project_dir/dist/main.js" \ pm2 start "$project_dir/dist/main.js" \
--name "$project" \ --name "$project" \
--env production \ --env production \
-- --port "$port" -- --port "$port"
else
pm2 restart "$project"
fi fi
else else
echo "$project sem build — rode: ./host-deploy.sh $project" echo "$project sem build — rode: ./host-deploy.sh $project"
@@ -62,7 +64,7 @@ EOF
done done
nginx -s reload 2>/dev/null || true nginx -s reload 2>/dev/null || true
echo "✓ Nginx atualizado" echo "✓ Nginx atualizado com rotas dos projetos"
pm2 save 2>/dev/null || true pm2 save 2>/dev/null || true
@@ -72,4 +74,4 @@ echo " Stack pronta!"
echo " Acesse: http://${DOMAIN:-localhost}/<projeto>" echo " Acesse: http://${DOMAIN:-localhost}/<projeto>"
echo "================================================" echo "================================================"
pm2 logs --raw pm2 logs --raw