fix(convex): mover cron jobs para API HTTP + crontab do Linux
Problema: - Cron jobs do Convex criam registros em _scheduled_job_logs - Convex self-hosted carrega TODAS as versoes em memoria - 1488 execucoes/dia = ~45k registros/mes acumulando - Uso de memoria chegando a 19GB, causando 12 OOM kills/dia Solucao: - Criar endpoints HTTP em /api/cron/* para substituir crons - Desabilitar crons no Convex (comentados em crons.ts) - Chamar endpoints via crontab do Linux Novos arquivos: - src/app/api/cron/chat-cleanup/route.ts - src/app/api/cron/usb-cleanup/route.ts - scripts-static/* (copiado da VPS para versionamento) Documentacao: - docs/OPERATIONS.md secao 12 com instrucoes do crontab 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e2dde8510a
commit
178c7d7341
10 changed files with 1357 additions and 19 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
37
scripts-static/bootstrap.ps1
Normal file
37
scripts-static/bootstrap.ps1
Normal file
|
|
@ -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
|
||||
398
scripts-static/comandos.json
Normal file
398
scripts-static/comandos.json
Normal file
|
|
@ -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 <nome_do_pacote>",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
32
scripts-static/docker-compose.yml
Normal file
32
scripts-static/docker-compose.yml
Normal file
|
|
@ -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
|
||||
28
scripts-static/index.html
Normal file
28
scripts-static/index.html
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!doctype html>
|
||||
<html lang="pt-BR">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>scripts.rever.com.br</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
:root { color-scheme: light dark; }
|
||||
body { font-family: system-ui, -apple-system, Segoe UI, sans-serif; max-width: 860px; margin: 40px auto; padding: 0 20px; line-height: 1.6; }
|
||||
code, pre { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
|
||||
pre { background: #111827; color: #e5e7eb; padding: 12px 14px; border-radius: 8px; overflow-x: auto; }
|
||||
h1 { margin-bottom: 0; }
|
||||
small { color: #6b7280; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Scripts de suporte</h1>
|
||||
<small>scripts.rever.com.br</small>
|
||||
<p>Menu automatizado para Windows (PowerShell 5+). O bootstrap baixa <code>menu.ps1</code> e <code>comandos.json</code> no diretório temporário e executa com política liberada apenas na sessão atual.</p>
|
||||
<h3>Uso rápido (um comando)</h3>
|
||||
<pre>irm https://scripts.rever.com.br/bootstrap.ps1 | iex</pre>
|
||||
<h3>Baixar arquivos manualmente</h3>
|
||||
<pre>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</pre>
|
||||
<p>Arquivos servidos via HTTPS pela VPS 154.12.253.40 (Traefik + nginx). Atualize os arquivos e redeploy para novas versões.</p>
|
||||
</body>
|
||||
</html>
|
||||
657
scripts-static/menu.ps1
Normal file
657
scripts-static/menu.ps1
Normal file
|
|
@ -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 <nome_do_pacote>",
|
||||
"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<num> 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<num>, 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<num>, 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
|
||||
}
|
||||
}
|
||||
53
src/app/api/cron/chat-cleanup/route.ts
Normal file
53
src/app/api/cron/chat-cleanup/route.ts
Normal file
|
|
@ -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 }
|
||||
)
|
||||
}
|
||||
}
|
||||
53
src/app/api/cron/usb-cleanup/route.ts
Normal file
53
src/app/api/cron/usb-cleanup/route.ts
Normal file
|
|
@ -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 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue