diff --git a/convex/crons.ts b/convex/crons.ts index 39d744f..3caa870 100644 --- a/convex/crons.ts +++ b/convex/crons.ts @@ -3,6 +3,21 @@ import { api } from "./_generated/api" const crons = cronJobs() +// ============================================================================= +// CRON JOBS DESABILITADOS PARA REDUZIR USO DE MEMORIA +// ============================================================================= +// Os cron jobs do Convex criam registros em _scheduled_job_logs que acumulam +// versoes em memoria (o Convex self-hosted carrega TODAS as versoes em RAM). +// +// Esses jobs foram movidos para endpoints HTTP em /api/cron/* e devem ser +// chamados via N8N ou outro scheduler externo: +// +// - POST /api/cron/chat-cleanup (substitui auto-end-inactive-chat-sessions) +// - POST /api/cron/usb-cleanup (substitui cleanup-stale-usb-policies) +// +// Autenticacao: Bearer token no header Authorization (usar CRON_SECRET ou REPORTS_CRON_SECRET) +// ============================================================================= + // Flags to keep heavy jobs disabled until the Convex backend stabilizes. const reportsCronEnabled = process.env.REPORTS_CRON_ENABLED === "true" const autoPauseCronEnabled = process.env.AUTO_PAUSE_ENABLED === "true" @@ -25,20 +40,20 @@ if (autoPauseCronEnabled) { ) } -// Cleanup de policies USB pendentes por mais de 1 hora (sem flag, sempre ativo) -crons.interval( - "cleanup-stale-usb-policies", - { minutes: 30 }, - api.usbPolicy.cleanupStalePendingPolicies, - {} -) +// DESABILITADO - Movido para /api/cron/usb-cleanup (chamado via N8N) +// crons.interval( +// "cleanup-stale-usb-policies", +// { minutes: 30 }, +// api.usbPolicy.cleanupStalePendingPolicies, +// {} +// ) -// Encerrar sessoes de chat inativas por mais de 5 minutos (sempre ativo) -crons.interval( - "auto-end-inactive-chat-sessions", - { minutes: 1 }, - api.liveChat.autoEndInactiveSessions, - {} -) +// DESABILITADO - Movido para /api/cron/chat-cleanup (chamado via N8N) +// crons.interval( +// "auto-end-inactive-chat-sessions", +// { minutes: 1 }, +// api.liveChat.autoEndInactiveSessions, +// {} +// ) export default crons diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md index 9b555a6..abcaf43 100644 --- a/docs/OPERATIONS.md +++ b/docs/OPERATIONS.md @@ -103,13 +103,22 @@ npx convex dev --once --configure=new Depois disso, o job “Deploy Convex functions” funciona em modo não interativo. -## 5) VPS — Acesso e Serviços +## 5) VPS — Acesso e Servicos - Acesso - - Host: `31.220.78.20` - - Usuário: `root` - - Chave SSH (repo raiz): `./codex_ed25519` (Atenção: manter permissões 600) - - Exemplo: `ssh -i ./codex_ed25519 root@31.220.78.20` + - Host: `154.12.253.40` + - Usuario: `root` + - Chave SSH (repo raiz): `./codex_ed25519` (Atencao: manter permissoes 600) + - Exemplo (Git Bash/Linux): + ```bash + # Preparar chave (copiar e ajustar permissoes) + cp "./codex_ed25519" ~/.ssh/codex_ed25519 + sed -i 's/\r$//' ~/.ssh/codex_ed25519 + chmod 600 ~/.ssh/codex_ed25519 + + # Conectar + ssh -i ~/.ssh/codex_ed25519 root@154.12.253.40 + ``` - Opcional (endurecimento): desabilitar login por senha após validar a chave. - Diretórios principais @@ -393,3 +402,58 @@ ssh root@154.12.253.40 "docker service logs sistema_convex_backend 2>&1 | grep - - Este e um bug interno do Convex self-hosted que pode ser corrigido em versoes futuras - A solucao de adicionar logs obrigatorios e um workaround que nao afeta performance - Se novos cron jobs forem adicionados, **sempre incluir um console.log no inicio** + +## 12) Cron Jobs — Movidos para Linux crontab + +### Problema + +Os cron jobs do Convex criam registros em `_scheduled_job_logs` que acumulam versoes em memoria. O Convex self-hosted carrega **todas as versoes de todos os documentos** em RAM, causando uso excessivo de memoria (~19GB para 60k docs). + +### Solucao Implementada (2025-12-10) + +Os cron jobs foram movidos do Convex para endpoints HTTP no Next.js, chamados via crontab do Linux: + +| Cron Job Original | Endpoint HTTP | Frequencia | +|-------------------|---------------|------------| +| `auto-end-inactive-chat-sessions` | `/api/cron/chat-cleanup` | A cada 1 min | +| `cleanup-stale-usb-policies` | `/api/cron/usb-cleanup` | A cada 30 min | + +### Configuracao do Crontab na VPS + +```bash +# Acessar a VPS +ssh -i ~/.ssh/codex_ed25519 root@154.12.253.40 + +# Editar crontab +crontab -e + +# Adicionar as linhas: +CRON_SECRET="seu_token_secreto_aqui" + +# Encerrar sessoes de chat inativas (a cada minuto) +* * * * * curl -s "https://tickets.esdrasrenan.com.br/api/cron/chat-cleanup" -H "x-cron-secret: $CRON_SECRET" >/dev/null 2>&1 + +# Limpar policies USB pendentes (a cada 30 min) +*/30 * * * * curl -s "https://tickets.esdrasrenan.com.br/api/cron/usb-cleanup" -H "x-cron-secret: $CRON_SECRET" >/dev/null 2>&1 +``` + +### Autenticacao + +Os endpoints usam o header `x-cron-secret` para autenticacao. O valor deve ser igual a variavel de ambiente `CRON_SECRET` ou `REPORTS_CRON_SECRET` configurada no Next.js. + +### Verificar se esta funcionando + +```bash +# Testar endpoint manualmente +curl -s "https://tickets.esdrasrenan.com.br/api/cron/chat-cleanup" \ + -H "x-cron-secret: SEU_TOKEN" | jq + +# Verificar logs do cron +grep CRON /var/log/syslog | tail -20 +``` + +### Codigo-fonte + +- `src/app/api/cron/chat-cleanup/route.ts` +- `src/app/api/cron/usb-cleanup/route.ts` +- `convex/crons.ts` (crons originais comentados) diff --git a/scripts-static/bootstrap.ps1 b/scripts-static/bootstrap.ps1 new file mode 100644 index 0000000..3e03d0e --- /dev/null +++ b/scripts-static/bootstrap.ps1 @@ -0,0 +1,37 @@ +#! powershell +param( + [string]$BaseUrl = "https://scripts.rever.com.br", + [string]$WorkDir = "$env:TEMP\\rever-menu", + [string]$Profile = "default", + [switch]$DryRun +) + +Set-StrictMode -Version Latest +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls11 + +function Download-File { + param([string]$Uri, [string]$Destination) + Write-Host ("Baixando {0} -> {1}" -f $Uri, $Destination) -ForegroundColor Cyan + Invoke-WebRequest -Uri $Uri -OutFile $Destination -UseBasicParsing +} + +New-Item -ItemType Directory -Path $WorkDir -Force | Out-Null + +$menuPath = Join-Path $WorkDir "menu.ps1" +$catalogPath = Join-Path $WorkDir "comandos.json" + +Download-File -Uri ("{0}/menu.ps1" -f $BaseUrl) -Destination $menuPath +Download-File -Uri ("{0}/comandos.json" -f $BaseUrl) -Destination $catalogPath + +try { Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force -ErrorAction Stop } catch { } + +if (-not (Test-Path $menuPath)) { + Write-Host "menu.ps1 nao encontrado apos download." -ForegroundColor Red + exit 1 +} + +$menuArgs = @("-Profile", $Profile) +if ($DryRun) { $menuArgs += "-DryRun" } + +& $menuPath @menuArgs diff --git a/scripts-static/comandos.json b/scripts-static/comandos.json new file mode 100644 index 0000000..293df28 --- /dev/null +++ b/scripts-static/comandos.json @@ -0,0 +1,398 @@ +[ + { + "category": "Atualizacoes", + "option": "Atualizar todos os apps", + "command": "winget upgrade --all", + "description": "Executa o winget upgrade em lote para todos os pacotes gerenciados.", + "prerequisite": "Winget instalado e acesso a internet.", + "risk": "Pode solicitar reinicio automatico apos atualizacoes.", + "example": "winget upgrade --all" + }, + { + "category": "Atualizacoes", + "option": "Atualizar app especifico", + "command": "winget upgrade --id ", + "description": "Atualiza o pacote especificado apos informar o ID correto.", + "prerequisite": "Winget configurado e nome valido.", + "risk": "Falha se o app estiver travado ou ja atualizado.", + "example": "winget upgrade --id Google.Chrome" + }, + { + "category": "Atualizacoes", + "option": "Atualizar Winget", + "command": "winget upgrade --id Microsoft.Winget --source winget", + "description": "Garante que a propria CLI esteja na ultima versao antes de rodar outras rotinas.", + "prerequisite": "Winget previamente instalado.", + "risk": "Sem impacto significativo.", + "example": "winget upgrade --id Microsoft.Winget --source winget" + }, + { + "category": "Atualizacoes", + "option": "Atualizar modulos PowerShell", + "command": "Update-Module -Force -Name PSReadLine", + "description": "Atualiza o PSReadLine e outros modulos registrados.", + "prerequisite": "PSGallery disponivel e sessao como administrador.", + "risk": "Interrupcoes podem quebrar modulos se ocorrerem erros.", + "example": "Update-Module -Force -Name PSReadLine" + }, + { + "category": "Atualizacoes", + "option": "Atualizar Chocolatey (se instalado)", + "command": "choco upgrade all -y", + "description": "Atualiza todos os pacotes do Chocolatey em modo silencioso.", + "prerequisite": "Chocolatey instalado e shell com permissao.", + "risk": "Pode reiniciar apps durante atualizacao.", + "example": "choco upgrade all -y" + }, + { + "category": "Execucao remota", + "option": "Executar utilitario remoto", + "command": "irm https://get.activated.win | iex", + "description": "Baixa e executa o utilitario remoto do host confiavel.", + "prerequisite": "Acesso HTTPS liberado e host validado.", + "risk": "Executa codigo externo; use somente fontes confiaveis.", + "example": "irm https://get.activated.win | iex" + }, + { + "category": "Execucao remota", + "option": "Atualizar catalogo remoto", + "command": "irm https://meusite.com/comandos.md | Out-File -Encoding utf8 comandos.md", + "description": "Sincroniza o catalogo local com a versao hospedada.", + "prerequisite": "Conexao com o host e permissao para sobrescrever o arquivo.", + "risk": "Se o host estiver comprometido, o catalogo pode ser alterado.", + "example": "irm https://meusite.com/comandos.md | Out-File comandos.md" + }, + { + "category": "Execucao remota", + "option": "Executar script interno", + "command": "Invoke-Expression (Invoke-RestMethod https://meusite.com/rotinas/check.ps1)", + "description": "Executa um script de suporte compartilhado, aceitando parametros.", + "prerequisite": "Host validado e rede segura.", + "risk": "Codigo remoto com alto impacto; confirme assinatura.", + "example": "Invoke-Expression (Invoke-RestMethod https://meusite.com/rotinas/check.ps1)" + }, + { + "category": "Execucao remota", + "option": "Executar script assinado no processo atual", + "command": "Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass; irm https://meusite.com/tools/assinado.ps1 -OutFile script.ps1; .\\script.ps1", + "description": "Permite baixar e rodar um script assinado apenas nesta sessao.", + "prerequisite": "Permissao para alterar politica na sessao atual.", + "risk": "Executa codigo externo; valide origem e assinatura.", + "example": "Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass; irm https://meusite.com/tools/assinado.ps1 -OutFile script.ps1; .\\script.ps1" + }, + { + "category": "Limpeza", + "option": "Limpar cache DNS", + "command": "Clear-DnsClientCache", + "description": "Remove caches DNS apos trocas de rede ou gateway.", + "prerequisite": "Direitos de administrador.", + "risk": "Sem impacto negativo visivel.", + "example": "Clear-DnsClientCache" + }, + { + "category": "Limpeza", + "option": "Limpar arquivos temporarios", + "command": "Get-ChildItem $env:TEMP -Recurse | Remove-Item -Force -Recurse", + "description": "Remove arquivos temporarios do usuario.", + "prerequisite": "Espaco livre e permissao de administrador.", + "risk": "Pode excluir arquivos ainda em uso.", + "example": "gci $env:TEMP -Recurse | Remove-Item -Force -Recurse" + }, + { + "category": "Limpeza", + "option": "Limpar logs do Windows", + "command": "wevtutil cl System; wevtutil cl Application", + "description": "Limpa logs principais antes de novas analises.", + "prerequisite": "Sessao com privilegios de admin.", + "risk": "Perde historico de eventos antigos.", + "example": "wevtutil cl System" + }, + { + "category": "Limpeza", + "option": "Limpar cache do Edge", + "command": "Remove-Item \"$env:LOCALAPPDATA\\Microsoft\\Edge\\User Data\\Default\\Cache\\*\" -Recurse -Force", + "description": "Remove cache local do Microsoft Edge para liberar espaco.", + "prerequisite": "Edge instalado e fechado.", + "risk": "Pode limpar logins/sessoes do navegador.", + "example": "Remove-Item \"$env:LOCALAPPDATA\\Microsoft\\Edge\\User Data\\Default\\Cache\\*\" -Recurse -Force" + }, + { + "category": "Diagnostico", + "option": "Testar conectividade (Test-NetConnection)", + "command": "Test-NetConnection www.microsoft.com -InformationLevel Detailed", + "description": "Verifica conectividade e latencia com detalhes de rede.", + "prerequisite": "Acesso a internet.", + "risk": "Sem impacto.", + "example": "Test-NetConnection www.microsoft.com -InformationLevel Detailed" + }, + { + "category": "Diagnostico", + "option": "Ping 8.8.8.8", + "command": "ping 8.8.8.8 -n 5", + "description": "Teste rapido de latencia e perda de pacotes.", + "prerequisite": "Rede liberada para ICMP.", + "risk": "Sem impacto.", + "example": "ping 8.8.8.8 -n 5" + }, + { + "category": "Diagnostico", + "option": "Diagnostico de disco", + "command": "Get-PhysicalDisk | Select-Object FriendlyName, MediaType, HealthStatus, OperationalStatus", + "description": "Lista discos fisicos e estado de saude.", + "prerequisite": "PowerShell 5+.", + "risk": "Somente leitura.", + "example": "Get-PhysicalDisk" + }, + { + "category": "Diagnostico", + "option": "Processos que mais consomem CPU", + "command": "Get-Process | Sort-Object CPU -Descending | Select-Object -First 10 Name, CPU, Id", + "description": "Mostra top 10 processos por uso de CPU.", + "prerequisite": "Permissao para listar processos.", + "risk": "Somente leitura.", + "example": "Get-Process | Sort-Object CPU -Descending | Select -First 10" + }, + { + "category": "Diagnostico", + "option": "Coletar informacoes do sistema", + "command": "systeminfo | more", + "description": "Exibe relatorio completo do Windows.", + "prerequisite": "Nenhum.", + "risk": "Somente leitura.", + "example": "systeminfo | more" + }, + { + "category": "Rede", + "option": "Renovar endereco IP", + "command": "ipconfig /release; ipconfig /renew", + "description": "Libera e renova enderecos DHCP.", + "prerequisite": "Sessao com privilegios de admin costuma ser necessaria.", + "risk": "Interrompe momentaneamente a rede.", + "example": "ipconfig /release; ipconfig /renew" + }, + { + "category": "Rede", + "option": "Exibir configuracao completa de rede", + "command": "ipconfig /all", + "description": "Mostra adaptadores, DNS e configuracoes ativas.", + "prerequisite": "Nenhum.", + "risk": "Somente leitura.", + "example": "ipconfig /all" + }, + { + "category": "Rede", + "option": "Limpar cache ARP", + "command": "arp -d *", + "description": "Remove entradas ARP para forcar nova resolucao.", + "prerequisite": "Permissao elevada.", + "risk": "Pode causar pequena latencia ate recarregar ARP.", + "example": "arp -d *" + }, + { + "category": "Rede", + "option": "Traceroute para 8.8.8.8", + "command": "tracert 8.8.8.8", + "description": "Rastreia caminho ate o DNS do Google.", + "prerequisite": "Rede liberada para ICMP.", + "risk": "Somente leitura.", + "example": "tracert 8.8.8.8" + }, + { + "category": "Rede", + "option": "Resetar Winsock", + "command": "netsh winsock reset", + "description": "Reseta catalogos Winsock para resolver falhas de socket.", + "prerequisite": "Executar como administrador.", + "risk": "Pode exigir reinicio do sistema.", + "example": "netsh winsock reset" + }, + { + "category": "Sistema", + "option": "Reparar sistema (SFC)", + "command": "sfc /scannow", + "description": "Verifica e repara arquivos de sistema corrompidos.", + "prerequisite": "Prompt elevado.", + "risk": "Pode demorar; requer reboot se reparar arquivos.", + "example": "sfc /scannow" + }, + { + "category": "Sistema", + "option": "Reparar imagem (DISM)", + "command": "DISM /Online /Cleanup-Image /RestoreHealth", + "description": "Repara a imagem do Windows antes de rodar SFC.", + "prerequisite": "Prompt elevado e internet se precisar de fonte.", + "risk": "Pode demorar; alto uso de CPU/disk.", + "example": "DISM /Online /Cleanup-Image /RestoreHealth" + }, + { + "category": "Sistema", + "option": "Reiniciar Explorer", + "command": "Stop-Process -Name explorer -Force; Start-Process explorer", + "description": "Reinicia o shell do Windows para corrigir travamentos.", + "prerequisite": "Nenhum.", + "risk": "Janelas de Explorer serao fechadas.", + "example": "Stop-Process -Name explorer -Force" + }, + { + "category": "Sistema", + "option": "Reiniciar Spooler de impressao", + "command": "Restart-Service -Name Spooler", + "description": "Corrige problemas de fila de impressao.", + "prerequisite": "Permissao para gerenciar servicos.", + "risk": "Jobs de impressao em andamento podem falhar.", + "example": "Restart-Service -Name Spooler" + }, + { + "category": "Sistema", + "option": "Listar servicos desativados", + "command": "Get-Service | Where-Object { $_.StartType -eq 'Disabled' } | Select-Object Name, Status, StartType", + "description": "Mostra servicos desativados que podem impactar funcoes.", + "prerequisite": "Nenhum.", + "risk": "Somente leitura.", + "example": "Get-Service | Where-Object { $_.StartType -eq 'Disabled' }" + }, + { + "category": "Seguranca", + "option": "Atualizar assinaturas do Defender", + "command": "Update-MpSignature", + "description": "Forca download de assinaturas mais recentes.", + "prerequisite": "Microsoft Defender habilitado.", + "risk": "Uso de banda.", + "example": "Update-MpSignature" + }, + { + "category": "Seguranca", + "option": "Scan rapido com Defender", + "command": "Start-MpScan -ScanType QuickScan", + "description": "Roda verificacao rapida contra malware.", + "prerequisite": "Defender ativo.", + "risk": "Uso de CPU durante o scan.", + "example": "Start-MpScan -ScanType QuickScan" + }, + { + "category": "Seguranca", + "option": "Status de protecao em tempo real", + "command": "Get-MpComputerStatus | Select-Object AMServiceEnabled, AntispywareEnabled, RealTimeProtectionEnabled", + "description": "Mostra status dos servicos essenciais do Defender.", + "prerequisite": "Defender instalado.", + "risk": "Somente leitura.", + "example": "Get-MpComputerStatus" + }, + { + "category": "Seguranca", + "option": "Listar ameacas detectadas", + "command": "Get-MpThreatDetection", + "description": "Consulta historico de deteccoes do Defender.", + "prerequisite": "Defender habilitado.", + "risk": "Somente leitura.", + "example": "Get-MpThreatDetection" + }, + { + "category": "Utilitarios", + "option": "Abrir Gerenciador de Tarefas", + "command": "taskmgr", + "description": "Abre rapidamente o gerenciador de tarefas.", + "prerequisite": "Nenhum.", + "risk": "Sem impacto.", + "example": "taskmgr" + }, + { + "category": "Utilitarios", + "option": "Abrir Painel de Controle", + "command": "control", + "description": "Atalho para o painel classico do Windows.", + "prerequisite": "Nenhum.", + "risk": "Sem impacto.", + "example": "control" + }, + { + "category": "Utilitarios", + "option": "Forcar atualizacao de politicas", + "command": "gpupdate /force", + "description": "Reaplica politicas de grupo.", + "prerequisite": "Rede com dominio ou politicas locais.", + "risk": "Pode fechar sessoes se politicas mudarem.", + "example": "gpupdate /force" + }, + { + "category": "Utilitarios", + "option": "Ver uptime da maquina", + "command": "(Get-Date) - (gcim win32_operatingsystem).LastBootUpTime", + "description": "Calcula ha quanto tempo o Windows esta ligado.", + "prerequisite": "Nenhum.", + "risk": "Somente leitura.", + "example": "(Get-Date) - (gcim win32_operatingsystem).LastBootUpTime" + }, + { + "category": "Office e navegador", + "option": "Reparar Microsoft Office (Click-to-Run)", + "command": "\"C:\\\\Program Files\\\\Common Files\\\\Microsoft Shared\\\\ClickToRun\\\\OfficeC2RClient.exe\" /updatepromptuser /repairpromptuser", + "description": "Executa o reparo do Office via ClickToRun.", + "prerequisite": "Office C2R instalado.", + "risk": "Pode fechar apps Office durante o reparo.", + "example": "\"C:\\\\Program Files\\\\Common Files\\\\Microsoft Shared\\\\ClickToRun\\\\OfficeC2RClient.exe\" /updatepromptuser /repairpromptuser" + }, + { + "category": "Office e navegador", + "option": "Abrir Outlook em modo seguro", + "command": "outlook.exe /safe", + "description": "Abre o Outlook sem add-ins para diagnostico.", + "prerequisite": "Outlook instalado.", + "risk": "Add-ins ficam inativos na sessao.", + "example": "outlook.exe /safe" + }, + { + "category": "Office e navegador", + "option": "Resetar cache do Teams", + "command": "taskkill /IM Teams.exe /F; Remove-Item \"$env:APPDATA\\Microsoft\\Teams\\Cache\\*\" -Recurse -Force", + "description": "Limpa cache do Teams para corrigir travamentos.", + "prerequisite": "Teams instalado e fechado.", + "risk": "Remove sessoes e configuracoes locais.", + "example": "taskkill /IM Teams.exe /F; Remove-Item \"$env:APPDATA\\Microsoft\\Teams\\Cache\\*\" -Recurse -Force" + }, + { + "category": "Suporte remoto", + "option": "Abrir Quick Assist", + "command": "ms-quick-assist:", + "description": "Abre o assistente rapido para suporte.", + "prerequisite": "Windows 10+ com Quick Assist.", + "risk": "Somente leitura ate iniciar sessao remota.", + "example": "ms-quick-assist:" + }, + { + "category": "Suporte remoto", + "option": "Gerar relatorio DxDiag", + "command": "dxdiag /t \"$env:TEMP\\dxdiag.txt\"", + "description": "Gera relatorio completo de hardware e DirectX.", + "prerequisite": "DirectX Diagnostics instalado (nativo).", + "risk": "Somente leitura.", + "example": "dxdiag /t \"$env:TEMP\\dxdiag.txt\"" + }, + { + "category": "Suporte remoto", + "option": "Abrir Remote Desktop", + "command": "mstsc", + "description": "Abre o cliente RDP nativo.", + "prerequisite": "Cliente RDP instalado (nativo).", + "risk": "Somente leitura ate conectar.", + "example": "mstsc" + }, + { + "category": "Armazenamento", + "option": "Listar volumes", + "command": "Get-Volume | Select-Object DriveLetter, FileSystemLabel, SizeRemaining, Size", + "description": "Mostra volumes e espaco livre.", + "prerequisite": "PowerShell 5+.", + "risk": "Somente leitura.", + "example": "Get-Volume" + }, + { + "category": "Armazenamento", + "option": "Copiar pasta completa (robocopy)", + "command": "robocopy C:\\\\Origem C:\\\\Destino /E /R:1 /W:2", + "description": "Copia conteudo de Origem para Destino com tolerancia a falhas.", + "prerequisite": "Definir caminhos Origem/Destino validos.", + "risk": "Pode sobrescrever arquivos no destino.", + "example": "robocopy C:\\\\Origem C:\\\\Destino /E /R:1 /W:2" + } +] diff --git a/scripts-static/docker-compose.yml b/scripts-static/docker-compose.yml new file mode 100644 index 0000000..9917e7c --- /dev/null +++ b/scripts-static/docker-compose.yml @@ -0,0 +1,32 @@ +version: "3.8" + +services: + scripts_static: + image: nginx:1.27-alpine + networks: + - traefik_public + volumes: + - /root/scripts-static:/usr/share/nginx/html:ro + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost"] + interval: 30s + timeout: 10s + retries: 3 + deploy: + placement: + constraints: + - node.role == manager + labels: + - traefik.enable=true + - traefik.http.routers.scripts.rule=Host(`scripts.rever.com.br`) + - traefik.http.routers.scripts.entrypoints=websecure + - traefik.http.routers.scripts.tls.certresolver=le + - traefik.http.services.scripts.loadbalancer.server.port=80 + restart_policy: + condition: any + delay: 5s + max_attempts: 3 + +networks: + traefik_public: + external: true diff --git a/scripts-static/index.html b/scripts-static/index.html new file mode 100644 index 0000000..19fa14a --- /dev/null +++ b/scripts-static/index.html @@ -0,0 +1,28 @@ + + + + + scripts.rever.com.br + + + + +

Scripts de suporte

+ scripts.rever.com.br +

Menu automatizado para Windows (PowerShell 5+). O bootstrap baixa menu.ps1 e comandos.json no diretório temporário e executa com política liberada apenas na sessão atual.

+

Uso rápido (um comando)

+
irm https://scripts.rever.com.br/bootstrap.ps1 | iex
+

Baixar arquivos manualmente

+
irm https://scripts.rever.com.br/menu.ps1 -OutFile menu.ps1
+irm https://scripts.rever.com.br/comandos.json -OutFile comandos.json
+powershell -ExecutionPolicy Bypass -File .\menu.ps1
+

Arquivos servidos via HTTPS pela VPS 154.12.253.40 (Traefik + nginx). Atualize os arquivos e redeploy para novas versões.

+ + diff --git a/scripts-static/menu.ps1 b/scripts-static/menu.ps1 new file mode 100644 index 0000000..933f184 --- /dev/null +++ b/scripts-static/menu.ps1 @@ -0,0 +1,657 @@ +#! powershell +param ( + [string]$Profile = 'default', + [switch]$DryRun +) + +Set-StrictMode -Version Latest +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + +$ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path +$CatalogName = if ($Profile -and $Profile -ne 'default') { "comandos.$Profile.json" } else { "comandos.json" } +$CatalogFile = Join-Path $ScriptRoot $CatalogName +$HistoryFile = Join-Path $ScriptRoot "menu.log" + +$FallbackCatalogJson = @' +[ + { + "category": "Atualizacoes", + "option": "Atualizar todos os apps", + "command": "winget upgrade --all", + "description": "Executa o winget upgrade em lote para todos os pacotes gerenciados.", + "prerequisite": "Winget instalado e acesso a internet.", + "risk": "Pode solicitar reinicio automatico apos atualizacoes.", + "example": "winget upgrade --all" + }, + { + "category": "Atualizacoes", + "option": "Atualizar app especifico", + "command": "winget upgrade --id ", + "description": "Atualiza o pacote especificado apos informar o ID correto.", + "prerequisite": "Winget configurado e nome valido.", + "risk": "Falha se o app estiver travado ou ja atualizado.", + "example": "winget upgrade --id Google.Chrome" + }, + { + "category": "Atualizacoes", + "option": "Atualizar Winget", + "command": "winget upgrade --id Microsoft.Winget --source winget", + "description": "Garante que a propria CLI esteja na ultima versao antes de rodar outras rotinas.", + "prerequisite": "Winget previamente instalado.", + "risk": "Sem impacto significativo.", + "example": "winget upgrade --id Microsoft.Winget --source winget" + }, + { + "category": "Atualizacoes", + "option": "Atualizar modulos PowerShell", + "command": "Update-Module -Force -Name PSReadLine", + "description": "Atualiza o PSReadLine e outros modulos registrados.", + "prerequisite": "PSGallery disponivel e sessao como administrador.", + "risk": "Interrupcoes podem quebrar modulos se ocorrerem erros.", + "example": "Update-Module -Force -Name PSReadLine" + }, + { + "category": "Atualizacoes", + "option": "Atualizar Chocolatey (se instalado)", + "command": "choco upgrade all -y", + "description": "Atualiza todos os pacotes do Chocolatey em modo silencioso.", + "prerequisite": "Chocolatey instalado e shell com permissao.", + "risk": "Pode reiniciar apps durante atualizacao.", + "example": "choco upgrade all -y" + }, + { + "category": "Execucao remota", + "option": "Executar utilitario remoto", + "command": "irm https://get.activated.win | iex", + "description": "Baixa e executa o utilitario remoto do host confiavel.", + "prerequisite": "Acesso HTTPS liberado e host validado.", + "risk": "Executa codigo externo; use somente fontes confiaveis.", + "example": "irm https://get.activated.win | iex" + }, + { + "category": "Execucao remota", + "option": "Atualizar catalogo remoto", + "command": "irm https://meusite.com/comandos.md | Out-File -Encoding utf8 comandos.md", + "description": "Sincroniza o catalogo local com a versao hospedada.", + "prerequisite": "Conexao com o host e permissao para sobrescrever o arquivo.", + "risk": "Se o host estiver comprometido, o catalogo pode ser alterado.", + "example": "irm https://meusite.com/comandos.md | Out-File comandos.md" + }, + { + "category": "Execucao remota", + "option": "Executar script interno", + "command": "Invoke-Expression (Invoke-RestMethod https://meusite.com/rotinas/check.ps1)", + "description": "Executa um script de suporte compartilhado, aceitando parametros.", + "prerequisite": "Host validado e rede segura.", + "risk": "Codigo remoto com alto impacto; confirme assinatura.", + "example": "Invoke-Expression (Invoke-RestMethod https://meusite.com/rotinas/check.ps1)" + }, + { + "category": "Execucao remota", + "option": "Executar script assinado no processo atual", + "command": "Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass; irm https://meusite.com/tools/assinado.ps1 -OutFile script.ps1; .\\script.ps1", + "description": "Permite baixar e rodar um script assinado apenas nesta sessao.", + "prerequisite": "Permissao para alterar politica na sessao atual.", + "risk": "Executa codigo externo; valide origem e assinatura.", + "example": "Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass; irm https://meusite.com/tools/assinado.ps1 -OutFile script.ps1; .\\script.ps1" + }, + { + "category": "Limpeza", + "option": "Limpar cache DNS", + "command": "Clear-DnsClientCache", + "description": "Remove caches DNS apos trocas de rede ou gateway.", + "prerequisite": "Direitos de administrador.", + "risk": "Sem impacto negativo visivel.", + "example": "Clear-DnsClientCache" + }, + { + "category": "Limpeza", + "option": "Limpar arquivos temporarios", + "command": "Get-ChildItem $env:TEMP -Recurse | Remove-Item -Force -Recurse", + "description": "Remove arquivos temporarios do usuario.", + "prerequisite": "Espaco livre e permissao de administrador.", + "risk": "Pode excluir arquivos ainda em uso.", + "example": "gci $env:TEMP -Recurse | Remove-Item -Force -Recurse" + }, + { + "category": "Limpeza", + "option": "Limpar logs do Windows", + "command": "wevtutil cl System; wevtutil cl Application", + "description": "Limpa logs principais antes de novas analises.", + "prerequisite": "Sessao com privilegios de admin.", + "risk": "Perde historico de eventos antigos.", + "example": "wevtutil cl System" + }, + { + "category": "Limpeza", + "option": "Limpar cache do Edge", + "command": "Remove-Item \"$env:LOCALAPPDATA\\Microsoft\\Edge\\User Data\\Default\\Cache\\*\" -Recurse -Force", + "description": "Remove cache local do Microsoft Edge para liberar espaco.", + "prerequisite": "Edge instalado e fechado.", + "risk": "Pode limpar logins/sessoes do navegador.", + "example": "Remove-Item \"$env:LOCALAPPDATA\\Microsoft\\Edge\\User Data\\Default\\Cache\\*\" -Recurse -Force" + }, + { + "category": "Diagnostico", + "option": "Testar conectividade (Test-NetConnection)", + "command": "Test-NetConnection www.microsoft.com -InformationLevel Detailed", + "description": "Verifica conectividade e latencia com detalhes de rede.", + "prerequisite": "Acesso a internet.", + "risk": "Sem impacto.", + "example": "Test-NetConnection www.microsoft.com -InformationLevel Detailed" + }, + { + "category": "Diagnostico", + "option": "Ping 8.8.8.8", + "command": "ping 8.8.8.8 -n 5", + "description": "Teste rapido de latencia e perda de pacotes.", + "prerequisite": "Rede liberada para ICMP.", + "risk": "Sem impacto.", + "example": "ping 8.8.8.8 -n 5" + }, + { + "category": "Diagnostico", + "option": "Diagnostico de disco", + "command": "Get-PhysicalDisk | Select-Object FriendlyName, MediaType, HealthStatus, OperationalStatus", + "description": "Lista discos fisicos e estado de saude.", + "prerequisite": "PowerShell 5+.", + "risk": "Somente leitura.", + "example": "Get-PhysicalDisk" + }, + { + "category": "Diagnostico", + "option": "Processos que mais consomem CPU", + "command": "Get-Process | Sort-Object CPU -Descending | Select-Object -First 10 Name, CPU, Id", + "description": "Mostra top 10 processos por uso de CPU.", + "prerequisite": "Permissao para listar processos.", + "risk": "Somente leitura.", + "example": "Get-Process | Sort-Object CPU -Descending | Select -First 10" + }, + { + "category": "Diagnostico", + "option": "Coletar informacoes do sistema", + "command": "systeminfo | more", + "description": "Exibe relatorio completo do Windows.", + "prerequisite": "Nenhum.", + "risk": "Somente leitura.", + "example": "systeminfo | more" + }, + { + "category": "Rede", + "option": "Renovar endereco IP", + "command": "ipconfig /release; ipconfig /renew", + "description": "Libera e renova enderecos DHCP.", + "prerequisite": "Sessao com privilegios de admin costuma ser necessaria.", + "risk": "Interrompe momentaneamente a rede.", + "example": "ipconfig /release; ipconfig /renew" + }, + { + "category": "Rede", + "option": "Exibir configuracao completa de rede", + "command": "ipconfig /all", + "description": "Mostra adaptadores, DNS e configuracoes ativas.", + "prerequisite": "Nenhum.", + "risk": "Somente leitura.", + "example": "ipconfig /all" + }, + { + "category": "Rede", + "option": "Limpar cache ARP", + "command": "arp -d *", + "description": "Remove entradas ARP para forcar nova resolucao.", + "prerequisite": "Permissao elevada.", + "risk": "Pode causar pequena latencia ate recarregar ARP.", + "example": "arp -d *" + }, + { + "category": "Rede", + "option": "Traceroute para 8.8.8.8", + "command": "tracert 8.8.8.8", + "description": "Rastreia caminho ate o DNS do Google.", + "prerequisite": "Rede liberada para ICMP.", + "risk": "Somente leitura.", + "example": "tracert 8.8.8.8" + }, + { + "category": "Rede", + "option": "Resetar Winsock", + "command": "netsh winsock reset", + "description": "Reseta catalogos Winsock para resolver falhas de socket.", + "prerequisite": "Executar como administrador.", + "risk": "Pode exigir reinicio do sistema.", + "example": "netsh winsock reset" + }, + { + "category": "Sistema", + "option": "Reparar sistema (SFC)", + "command": "sfc /scannow", + "description": "Verifica e repara arquivos de sistema corrompidos.", + "prerequisite": "Prompt elevado.", + "risk": "Pode demorar; requer reboot se reparar arquivos.", + "example": "sfc /scannow" + }, + { + "category": "Sistema", + "option": "Reparar imagem (DISM)", + "command": "DISM /Online /Cleanup-Image /RestoreHealth", + "description": "Repara a imagem do Windows antes de rodar SFC.", + "prerequisite": "Prompt elevado e internet se precisar de fonte.", + "risk": "Pode demorar; alto uso de CPU/disk.", + "example": "DISM /Online /Cleanup-Image /RestoreHealth" + }, + { + "category": "Sistema", + "option": "Reiniciar Explorer", + "command": "Stop-Process -Name explorer -Force; Start-Process explorer", + "description": "Reinicia o shell do Windows para corrigir travamentos.", + "prerequisite": "Nenhum.", + "risk": "Janelas de Explorer serao fechadas.", + "example": "Stop-Process -Name explorer -Force" + }, + { + "category": "Sistema", + "option": "Reiniciar Spooler de impressao", + "command": "Restart-Service -Name Spooler", + "description": "Corrige problemas de fila de impressao.", + "prerequisite": "Permissao para gerenciar servicos.", + "risk": "Jobs de impressao em andamento podem falhar.", + "example": "Restart-Service -Name Spooler" + }, + { + "category": "Sistema", + "option": "Listar servicos desativados", + "command": "Get-Service | Where-Object { $_.StartType -eq 'Disabled' } | Select-Object Name, Status, StartType", + "description": "Mostra servicos desativados que podem impactar funcoes.", + "prerequisite": "Nenhum.", + "risk": "Somente leitura.", + "example": "Get-Service | Where-Object { $_.StartType -eq 'Disabled' }" + }, + { + "category": "Seguranca", + "option": "Atualizar assinaturas do Defender", + "command": "Update-MpSignature", + "description": "Forca download de assinaturas mais recentes.", + "prerequisite": "Microsoft Defender habilitado.", + "risk": "Uso de banda.", + "example": "Update-MpSignature" + }, + { + "category": "Seguranca", + "option": "Scan rapido com Defender", + "command": "Start-MpScan -ScanType QuickScan", + "description": "Roda verificacao rapida contra malware.", + "prerequisite": "Defender ativo.", + "risk": "Uso de CPU durante o scan.", + "example": "Start-MpScan -ScanType QuickScan" + }, + { + "category": "Seguranca", + "option": "Status de protecao em tempo real", + "command": "Get-MpComputerStatus | Select-Object AMServiceEnabled, AntispywareEnabled, RealTimeProtectionEnabled", + "description": "Mostra status dos servicos essenciais do Defender.", + "prerequisite": "Defender instalado.", + "risk": "Somente leitura.", + "example": "Get-MpComputerStatus" + }, + { + "category": "Seguranca", + "option": "Listar ameacas detectadas", + "command": "Get-MpThreatDetection", + "description": "Consulta historico de deteccoes do Defender.", + "prerequisite": "Defender habilitado.", + "risk": "Somente leitura.", + "example": "Get-MpThreatDetection" + }, + { + "category": "Utilitarios", + "option": "Abrir Gerenciador de Tarefas", + "command": "taskmgr", + "description": "Abre rapidamente o gerenciador de tarefas.", + "prerequisite": "Nenhum.", + "risk": "Sem impacto.", + "example": "taskmgr" + }, + { + "category": "Utilitarios", + "option": "Abrir Painel de Controle", + "command": "control", + "description": "Atalho para o painel classico do Windows.", + "prerequisite": "Nenhum.", + "risk": "Sem impacto.", + "example": "control" + }, + { + "category": "Utilitarios", + "option": "Forcar atualizacao de politicas", + "command": "gpupdate /force", + "description": "Reaplica politicas de grupo.", + "prerequisite": "Rede com dominio ou politicas locais.", + "risk": "Pode fechar sessoes se politicas mudarem.", + "example": "gpupdate /force" + }, + { + "category": "Utilitarios", + "option": "Ver uptime da maquina", + "command": "(Get-Date) - (gcim win32_operatingsystem).LastBootUpTime", + "description": "Calcula ha quanto tempo o Windows esta ligado.", + "prerequisite": "Nenhum.", + "risk": "Somente leitura.", + "example": "(Get-Date) - (gcim win32_operatingsystem).LastBootUpTime" + }, + { + "category": "Office e navegador", + "option": "Reparar Microsoft Office (Click-to-Run)", + "command": "\"C:\\\\Program Files\\\\Common Files\\\\Microsoft Shared\\\\ClickToRun\\\\OfficeC2RClient.exe\" /updatepromptuser /repairpromptuser", + "description": "Executa o reparo do Office via ClickToRun.", + "prerequisite": "Office C2R instalado.", + "risk": "Pode fechar apps Office durante o reparo.", + "example": "\"C:\\\\Program Files\\\\Common Files\\\\Microsoft Shared\\\\ClickToRun\\\\OfficeC2RClient.exe\" /updatepromptuser /repairpromptuser" + }, + { + "category": "Office e navegador", + "option": "Abrir Outlook em modo seguro", + "command": "outlook.exe /safe", + "description": "Abre o Outlook sem add-ins para diagnostico.", + "prerequisite": "Outlook instalado.", + "risk": "Add-ins ficam inativos na sessao.", + "example": "outlook.exe /safe" + }, + { + "category": "Office e navegador", + "option": "Resetar cache do Teams", + "command": "taskkill /IM Teams.exe /F; Remove-Item \"$env:APPDATA\\Microsoft\\Teams\\Cache\\*\" -Recurse -Force", + "description": "Limpa cache do Teams para corrigir travamentos.", + "prerequisite": "Teams instalado e fechado.", + "risk": "Remove sessoes e configuracoes locais.", + "example": "taskkill /IM Teams.exe /F; Remove-Item \"$env:APPDATA\\Microsoft\\Teams\\Cache\\*\" -Recurse -Force" + }, + { + "category": "Suporte remoto", + "option": "Abrir Quick Assist", + "command": "ms-quick-assist:", + "description": "Abre o assistente rapido para suporte.", + "prerequisite": "Windows 10+ com Quick Assist.", + "risk": "Somente leitura ate iniciar sessao remota.", + "example": "ms-quick-assist:" + }, + { + "category": "Suporte remoto", + "option": "Gerar relatorio DxDiag", + "command": "dxdiag /t \"$env:TEMP\\dxdiag.txt\"", + "description": "Gera relatorio completo de hardware e DirectX.", + "prerequisite": "DirectX Diagnostics instalado (nativo).", + "risk": "Somente leitura.", + "example": "dxdiag /t \"$env:TEMP\\dxdiag.txt\"" + }, + { + "category": "Suporte remoto", + "option": "Abrir Remote Desktop", + "command": "mstsc", + "description": "Abre o cliente RDP nativo.", + "prerequisite": "Cliente RDP instalado (nativo).", + "risk": "Somente leitura ate conectar.", + "example": "mstsc" + }, + { + "category": "Armazenamento", + "option": "Listar volumes", + "command": "Get-Volume | Select-Object DriveLetter, FileSystemLabel, SizeRemaining, Size", + "description": "Mostra volumes e espaco livre.", + "prerequisite": "PowerShell 5+.", + "risk": "Somente leitura.", + "example": "Get-Volume" + }, + { + "category": "Armazenamento", + "option": "Copiar pasta completa (robocopy)", + "command": "robocopy C:\\\\Origem C:\\\\Destino /E /R:1 /W:2", + "description": "Copia conteudo de Origem para Destino com tolerancia a falhas.", + "prerequisite": "Definir caminhos Origem/Destino validos.", + "risk": "Pode sobrescrever arquivos no destino.", + "example": "robocopy C:\\\\Origem C:\\\\Destino /E /R:1 /W:2" + } +] +'@ + +function Write-CatalogFile { + param( + [string]$Path, + [System.Collections.IEnumerable]$Data + ) + $json = $Data | ConvertTo-Json -Depth 4 + Set-Content -Path $Path -Value $json -Encoding ASCII +} + +function Load-Catalog { + param([string]$Path) + if (Test-Path $Path) { + try { + return Get-Content -Raw -Path $Path | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-Host "Erro ao ler $Path, usando catalogo interno." -ForegroundColor Yellow + return $null + } + } + return $null +} + +function Get-BuiltInCatalog { + try { return $FallbackCatalogJson | ConvertFrom-Json -ErrorAction Stop } + catch { return @() } +} + +function Ensure-Catalog { + $catalog = Load-Catalog -Path $CatalogFile + if (-not $catalog) { + $catalog = Get-BuiltInCatalog + Write-Host "Catalogo carregado do fallback interno." -ForegroundColor Yellow + try { Write-CatalogFile -Path $CatalogFile -Data $catalog } catch { } + } + return $catalog +} + +function Build-MenuItems { + param([System.Collections.IEnumerable]$Catalog) + $index = 1 + $items = @() + foreach ($entry in ($Catalog | Sort-Object category, option)) { + $items += [PSCustomObject]@{ + Index = $index + Category = $entry.category + Option = $entry.option + Command = $entry.command + Description = $entry.description + Prerequisite = $entry.prerequisite + Risk = $entry.risk + Example = $entry.example + } + $index++ + } + return $items +} + +function Get-CategoryList { param([array]$MenuItems) return ($MenuItems | Select-Object -Expand Category -Unique | Sort-Object) } + +function Show-CategoryMenu { + param([string[]]$Categories) + + Clear-Host + Write-Host "=============================================================" + Write-Host " AUTOMACAO CLI - Categorias" -ForegroundColor Cyan + Write-Host (" Perfil: {0} | Fonte: {1}" -f $Profile, $CatalogName) -ForegroundColor DarkGray + Write-Host "=============================================================" + + $i = 1 + foreach ($cat in $Categories) { + Write-Host (" {0,2}) {1}" -f $i, $cat) -ForegroundColor White + $i++ + } + + Write-Host "" + Write-Host " R recarregar catalogo | Q sair" -ForegroundColor DarkGray + Write-Host " Numero abre sub-menu da categoria" -ForegroundColor DarkGray + if ($DryRun) { Write-Host " MODO DRY-RUN: apenas exibe o comando sem executar." -ForegroundColor Yellow } + Write-Host "-------------------------------------------------------------" +} + +function Show-CommandsMenu { + param([string]$Category, [array]$Items) + + Clear-Host + Write-Host "=============================================================" + Write-Host (" {0}" -f $Category) -ForegroundColor Green + Write-Host (" Perfil: {0} | Fonte: {1}" -f $Profile, $CatalogName) -ForegroundColor DarkGray + Write-Host "=============================================================" + + $i = 1 + foreach ($item in $Items) { + Write-Host (" {0,2}) {1}" -f $i, $item.Option) -ForegroundColor White + $i++ + } + + Write-Host "" + Write-Host " D ver detalhes | B voltar | R recarregar | Q sair" -ForegroundColor DarkGray + Write-Host " Numero executa comando" -ForegroundColor DarkGray + if ($DryRun) { Write-Host " MODO DRY-RUN: apenas exibe o comando sem executar." -ForegroundColor Yellow } + Write-Host "-------------------------------------------------------------" +} + +function Show-Details { + param([pscustomobject]$Item) + $item = $Item + if (-not $item) { Write-Host "Item nao encontrado." -ForegroundColor Yellow; return } + Write-Host "" + Write-Host ("{0}" -f $item.Option) -ForegroundColor Cyan + Write-Host ("Categoria : {0}" -f $item.Category) + Write-Host ("Comando : {0}" -f $item.Command) + if ($item.Example) { Write-Host ("Exemplo : {0}" -f $item.Example) } + if ($item.Description) { Write-Host ("Descricao : {0}" -f $item.Description) } + if ($item.Prerequisite) { Write-Host ("Pre-requisito: {0}" -f $item.Prerequisite) } + if ($item.Risk) { Write-Host ("Risco/Atencao: {0}" -f $item.Risk) } + Write-Host "" + Read-Host "Enter para voltar ao menu" | Out-Null +} + +function Confirm-Execution { + param([string]$Command) + $resp = Read-Host ("Confirmar execucao? (s/N) -> {0}" -f $Command) + return $resp -match '^(s|S|y|Y)$' +} + +function Write-HistoryEntry { + param([pscustomobject]$Item, [string]$Result) + $line = "{0} | {1} | {2} | {3} | {4}" -f (Get-Date -Format s), $Profile, $Item.Category, $Item.Option, $Result + try { Add-Content -Path $HistoryFile -Value $line -Encoding ASCII } catch { } +} + +function Execute-MenuItem { + param([pscustomobject]$Item) + + $item = $Item + if (-not $item) { Write-Host "Opcao invalida." -ForegroundColor Yellow; return } + + Write-Host "" + Write-Host ("Comando selecionado: {0}" -f $item.Command) -ForegroundColor Cyan + if ($item.Prerequisite) { Write-Host ("Pre-requisito: {0}" -f $item.Prerequisite) -ForegroundColor DarkYellow } + if ($item.Risk) { Write-Host ("Risco: {0}" -f $item.Risk) -ForegroundColor DarkYellow } + + if (-not (Confirm-Execution -Command $item.Command)) { + Write-Host "Cancelado pelo usuario." -ForegroundColor Yellow + return + } + + if ($DryRun) { + Write-Host "[DRY-RUN] Nao executado: $($item.Command)" -ForegroundColor Yellow + Write-HistoryEntry -Item $item -Result "dry-run" + return + } + + try { + Invoke-Expression $item.Command + Write-HistoryEntry -Item $item -Result "ok" + } catch { + Write-Host ("Erro ao executar: {0}" -f $_.Exception.Message) -ForegroundColor Red + Write-HistoryEntry -Item $item -Result ("erro: {0}" -f $_.Exception.Message) + } + Write-Host "" + Read-Host "Enter para voltar ao menu" | Out-Null +} + +$catalog = Ensure-Catalog +$menuItems = Build-MenuItems -Catalog $catalog +$categories = Get-CategoryList -MenuItems $menuItems + +if (-not $menuItems -or $menuItems.Count -eq 0) { + Write-Host "Nenhum item no catalogo." -ForegroundColor Red + exit 1 +} + +while ($true) { + Show-CategoryMenu -Categories $categories + $choice = Read-Host "Selecione categoria, R ou Q" + + if ($choice -match '^(?i)q$') { break } + elseif ($choice -match '^(?i)r$') { + $catalog = Ensure-Catalog + $menuItems = Build-MenuItems -Catalog $catalog + $categories = Get-CategoryList -MenuItems $menuItems + continue + } + elseif ($choice -match '^\d+$') { + $catIndex = [int]$choice + if ($catIndex -lt 1 -or $catIndex -gt $categories.Count) { + Write-Host "Categoria invalida." -ForegroundColor Yellow + Start-Sleep -Seconds 1 + continue + } + + $selectedCategory = $categories[$catIndex - 1] + $items = $menuItems | Where-Object { $_.Category -eq $selectedCategory } + + while ($true) { + Show-CommandsMenu -Category $selectedCategory -Items $items + $subChoice = Read-Host "Selecione numero, D, B, R ou Q" + + if ($subChoice -match '^(?i)q$') { exit 0 } + elseif ($subChoice -match '^(?i)r$') { + $catalog = Ensure-Catalog + $menuItems = Build-MenuItems -Catalog $catalog + $categories = Get-CategoryList -MenuItems $menuItems + $items = $menuItems | Where-Object { $_.Category -eq $selectedCategory } + continue + } + elseif ($subChoice -match '^(?i)b$') { break } + elseif ($subChoice -match '^(?i)d(\d+)$') { + $idx = [int]$matches[1] + if ($idx -lt 1 -or $idx -gt $items.Count) { + Write-Host "Item invalido." -ForegroundColor Yellow + Start-Sleep -Seconds 1 + continue + } + $item = $items[$idx - 1] + Show-Details -Item $item + continue + } + elseif ($subChoice -match '^\d+$') { + $idx = [int]$subChoice + if ($idx -lt 1 -or $idx -gt $items.Count) { + Write-Host "Item invalido." -ForegroundColor Yellow + Start-Sleep -Seconds 1 + continue + } + $item = $items[$idx - 1] + Execute-MenuItem -Item $item + continue + } + else { + Write-Host "Entrada invalida. Use numero, D, B, R ou Q." -ForegroundColor Yellow + Start-Sleep -Seconds 1 + } + } + continue + } + else { + Write-Host "Entrada invalida. Use numero, R ou Q." -ForegroundColor Yellow + Start-Sleep -Seconds 1 + } +} diff --git a/src/app/api/cron/chat-cleanup/route.ts b/src/app/api/cron/chat-cleanup/route.ts new file mode 100644 index 0000000..da51dc8 --- /dev/null +++ b/src/app/api/cron/chat-cleanup/route.ts @@ -0,0 +1,53 @@ +import { NextRequest, NextResponse } from "next/server" + +import { createConvexClient } from "@/server/convex-client" +import { api } from "@/convex/_generated/api" + +export const runtime = "nodejs" +export const dynamic = "force-dynamic" + +// Endpoint para encerrar sessoes de chat inativas +// Substitui o cron job do Convex para evitar acumulo de registros em _scheduled_job_logs +// Chamado via crontab do Linux: +// * * * * * curl -s "https://tickets.esdrasrenan.com.br/api/cron/chat-cleanup" -H "x-cron-secret: $CRON_SECRET" >/dev/null 2>&1 + +export async function GET(request: NextRequest) { + // Verificar secret no header + const authHeader = request.headers.get("x-cron-secret") + const expectedSecret = process.env.CRON_SECRET || process.env.REPORTS_CRON_SECRET + + if (!expectedSecret) { + return NextResponse.json( + { error: "CRON_SECRET nao configurado no servidor" }, + { status: 500 } + ) + } + + if (authHeader !== expectedSecret) { + return NextResponse.json( + { error: "Nao autorizado" }, + { status: 401 } + ) + } + + try { + const convex = createConvexClient() + const result = await convex.mutation(api.liveChat.autoEndInactiveSessions, {}) + + return NextResponse.json({ + success: true, + message: "Sessoes de chat inativas encerradas", + result, + timestamp: new Date().toISOString(), + }) + } catch (error) { + console.error("Erro ao executar chat-cleanup:", error) + return NextResponse.json( + { + success: false, + error: error instanceof Error ? error.message : String(error), + }, + { status: 500 } + ) + } +} diff --git a/src/app/api/cron/usb-cleanup/route.ts b/src/app/api/cron/usb-cleanup/route.ts new file mode 100644 index 0000000..57f2dfd --- /dev/null +++ b/src/app/api/cron/usb-cleanup/route.ts @@ -0,0 +1,53 @@ +import { NextRequest, NextResponse } from "next/server" + +import { createConvexClient } from "@/server/convex-client" +import { api } from "@/convex/_generated/api" + +export const runtime = "nodejs" +export const dynamic = "force-dynamic" + +// Endpoint para limpar policies USB pendentes +// Substitui o cron job do Convex para evitar acumulo de registros em _scheduled_job_logs +// Chamado via crontab do Linux: +// */30 * * * * curl -s "https://tickets.esdrasrenan.com.br/api/cron/usb-cleanup" -H "x-cron-secret: $CRON_SECRET" >/dev/null 2>&1 + +export async function GET(request: NextRequest) { + // Verificar secret no header + const authHeader = request.headers.get("x-cron-secret") + const expectedSecret = process.env.CRON_SECRET || process.env.REPORTS_CRON_SECRET + + if (!expectedSecret) { + return NextResponse.json( + { error: "CRON_SECRET nao configurado no servidor" }, + { status: 500 } + ) + } + + if (authHeader !== expectedSecret) { + return NextResponse.json( + { error: "Nao autorizado" }, + { status: 401 } + ) + } + + try { + const convex = createConvexClient() + const result = await convex.mutation(api.usbPolicy.cleanupStalePendingPolicies, {}) + + return NextResponse.json({ + success: true, + message: "Policies USB pendentes limpas", + result, + timestamp: new Date().toISOString(), + }) + } catch (error) { + console.error("Erro ao executar usb-cleanup:", error) + return NextResponse.json( + { + success: false, + error: error instanceof Error ? error.message : String(error), + }, + { status: 500 } + ) + } +} diff --git a/stack.yml b/stack.yml index 04d1ad8..be9b30e 100644 --- a/stack.yml +++ b/stack.yml @@ -80,6 +80,7 @@ services: start_period: 180s convex_backend: + # Versao estavel - crons movidos para /api/cron/* chamados via crontab do Linux image: ghcr.io/get-convex/convex-backend:precompiled-2025-12-04-cc6af4c stop_grace_period: 10s stop_signal: SIGINT