From 163b89aca58bdea130e6348d1d51329b6c2e8775 Mon Sep 17 00:00:00 2001 From: Juancjc Date: Sat, 18 Apr 2026 14:45:51 -0500 Subject: [PATCH] =?UTF-8?q?Atualiza=20scripts=20e=20configura=C3=A7=C3=B5e?= =?UTF-8?q?s=20do=20projeto=20para=20melhorar=20o=20gerenciamento=20de=20p?= =?UTF-8?q?rojetos=20e=20a=20estrutura=20do=20reposit=C3=B3rio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 2 +- README.md | 170 ++++++++++++++++++++++++------------------ docker-compose.yml | 4 +- host-deploy.sh | 2 +- host-remove.sh | 50 +++++++++++++ host-status.sh | 65 ++++++++++++++++ nginx/nginx.conf | 2 +- scripts/deploy.sh | 13 ++-- scripts/hot-deploy.sh | 14 ++++ scripts/hot-remove.sh | 55 ++++++++++++++ scripts/start.sh | 20 ++--- 11 files changed, 304 insertions(+), 93 deletions(-) create mode 100644 host-remove.sh create mode 100644 host-status.sh create mode 100644 scripts/hot-deploy.sh create mode 100644 scripts/hot-remove.sh diff --git a/Dockerfile b/Dockerfile index cea2b89..af70299 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ RUN mkdir -p /home/deploy/projects \ /var/log/nginx \ /run/nginx -# Copy scripts +# Copy scripts internos do container COPY scripts/ /home/deploy/scripts/ RUN chmod +x /home/deploy/scripts/*.sh diff --git a/README.md b/README.md index 70bb2ef..54bbc14 100644 --- a/README.md +++ b/README.md @@ -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/ ├── 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/ +├── projects/ ← seus projetos NestJS (volume) +│ ├── projeto1/ +│ └── projeto2/ +├── nginx-dynamic/ ← configs nginx persistidas (volume) +└── pm2/ ← estado do PM2 persistido (volume) ``` --- -## Setup inicial - -### 1. No servidor (VPS), clone este repo +## Setup inicial no VPS ```bash +# 1. Clone este repositório git clone /home/deploy/nest-stack + +# 2. Crie as pastas necessárias 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 -# No HOST (fora do container) 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 -./home/deploy/nest-stack/host-deploy.sh projeto1 +# Coloca o .env do projeto +nano /home/deploy/projects/projeto1/.env -# 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 +# Deploya /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** -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) +Pode reiniciar pelo Portainer sem medo — nada é perdido: + +- ✅ 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 ```bash -# Ver logs do container +# Logs do container docker logs nestjs-stack -f -# Ver processos PM2 rodando +# Processos PM2 docker exec nestjs-stack pm2 list -# Ver logs de um projeto específico +# Logs de um projeto docker exec nestjs-stack pm2 logs projeto1 # Entrar no container 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 ``` --- -## Regras de porta +## Portas internas -| Projeto | Porta interna | -|---------------|---------------| -| projeto1 | 3001 | -| projeto2 | 3002 | -| projeto3 | 3003 | -| projetoN | 300N | +| Projeto | Porta | +|------------|--------| +| projeto1 | 3001 | +| projeto2 | 3002 | +| projeto3 | 3003 | -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. -Seu app recebe as rotas normalmente como `/`. - -**Único requisito:** seu `main.ts` deve aceitar a porta via variável de ambiente ou argumento: +O `main.ts` deve aceitar porta via argumento ou variável de ambiente: ```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; + const port = + process.argv.find((_, i, a) => a[i - 1] === '--port') || + process.env.PORT || + 3000; await app.listen(port); } bootstrap(); diff --git a/docker-compose.yml b/docker-compose.yml index 29edeef..a09bf9c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,8 +11,8 @@ services: - "3451:80" volumes: - /home/deploy/projects:/home/deploy/projects - - /home/deploy/nginx-dynamic:/etc/nginx/sites-dynamic # ← persiste configs nginx - - /home/deploy/pm2:/root/.pm2 # ← persiste estado do PM2 + - /home/deploy/nginx-dynamic:/etc/nginx/sites-dynamic + - /home/deploy/pm2:/root/.pm2 environment: - PROJECTS_DIR=/home/deploy/projects - BASE_PORT=3000 diff --git a/host-deploy.sh b/host-deploy.sh index ec0874b..1315e08 100644 --- a/host-deploy.sh +++ b/host-deploy.sh @@ -1,5 +1,5 @@ #!/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 # Ou: ./host-deploy.sh (deploya todos) diff --git a/host-remove.sh b/host-remove.sh new file mode 100644 index 0000000..547eb22 --- /dev/null +++ b/host-remove.sh @@ -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 "================================================" diff --git a/host-status.sh b/host-status.sh new file mode 100644 index 0000000..07eed8e --- /dev/null +++ b/host-status.sh @@ -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 $PROJECTS_DIR/" +fi + +echo "================================================" +echo " Container: $(docker inspect -f '{{.State.Status}}' nestjs-stack 2>/dev/null || echo 'não encontrado')" +echo "================================================" +echo "" diff --git a/nginx/nginx.conf b/nginx/nginx.conf index fe85966..7325fd5 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -30,7 +30,7 @@ http { 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; } } diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 0a990f8..a76b4ff 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -1,6 +1,6 @@ #!/bin/bash -# deploy.sh — deploya um projeto ou todos -# Uso dentro do container: +# deploy.sh — deploya um projeto ou todos (roda DENTRO do container) +# Uso: # /home/deploy/scripts/deploy.sh → deploya todos # /home/deploy/scripts/deploy.sh meu-projeto → deploya só um @@ -23,6 +23,11 @@ deploy_project() { cd "$project_dir" + # Mantém .env existente + if [ -f "$project_dir/.env" ]; then + echo " → .env encontrado, mantendo..." + fi + # Instala dependências echo " → npm install..." npm install --prefer-offline 2>&1 | tail -3 @@ -72,8 +77,6 @@ EOF # ------------------------------------------- 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 @@ -86,7 +89,6 @@ if [ -n "$TARGET" ]; then 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 @@ -97,7 +99,6 @@ else done fi -# Recarrega nginx echo "" echo "→ Recarregando Nginx..." nginx -s reload diff --git a/scripts/hot-deploy.sh b/scripts/hot-deploy.sh new file mode 100644 index 0000000..7ac892f --- /dev/null +++ b/scripts/hot-deploy.sh @@ -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 \ No newline at end of file diff --git a/scripts/hot-remove.sh b/scripts/hot-remove.sh new file mode 100644 index 0000000..453eba9 --- /dev/null +++ b/scripts/hot-remove.sh @@ -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 "================================================" \ No newline at end of file diff --git a/scripts/start.sh b/scripts/start.sh index d6d6229..bbb34e7 100644 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -1,4 +1,7 @@ #!/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}" BASE_PORT="${BASE_PORT:-3000}" @@ -25,12 +28,11 @@ for project_dir in "$PROJECTS_DIR"/*/; do if [ -f "$project_dir/dist/main.js" ]; then echo " → Subindo $project na porta $port" - # Salva porta 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 - echo " → Recriando config nginx para $project" + echo " → Criando config nginx para $project" cat > "/etc/nginx/sites-dynamic/${project}.conf" < /dev/null 2>&1; then + # Sobe no PM2 se não estiver rodando, senão reinicia + if pm2 describe "$project" > /dev/null 2>&1; then + pm2 restart "$project" + else pm2 start "$project_dir/dist/main.js" \ --name "$project" \ --env production \ -- --port "$port" - else - pm2 restart "$project" fi else echo " ⚠ $project sem build — rode: ./host-deploy.sh $project" @@ -62,7 +64,7 @@ EOF done nginx -s reload 2>/dev/null || true -echo "✓ Nginx atualizado" +echo "✓ Nginx atualizado com rotas dos projetos" pm2 save 2>/dev/null || true @@ -72,4 +74,4 @@ echo " Stack pronta!" echo " Acesse: http://${DOMAIN:-localhost}/" echo "================================================" -pm2 logs --raw \ No newline at end of file +pm2 logs --raw