460 lines
14 KiB
PowerShell
460 lines
14 KiB
PowerShell
# Sistema de Monitoramento de Arquivos para RDO-C
|
|
# Monitora mudanças em tempo real e executa ações automáticas
|
|
# Uso: .\scripts\file-watcher.ps1 [-Action <sync|notify>] [-Interval <seconds>]
|
|
|
|
param(
|
|
[string]$Action = "sync", # sync, notify, all
|
|
[int]$Interval = 5, # Intervalo em segundos para verificação
|
|
[string]$ConfigFile = "auto-sync-config.json",
|
|
[switch]$Daemon, # Executar como daemon
|
|
[switch]$Verbose, # Log detalhado
|
|
[string]$LogLevel = "info" # debug, info, warn, error
|
|
)
|
|
|
|
# Configurações
|
|
$script:Config = $null
|
|
$script:WatcherJobs = @()
|
|
$script:LastSync = Get-Date
|
|
$script:IsRunning = $false
|
|
$script:LogFile = "logs\file-watcher.log"
|
|
$script:StatsFile = "logs\watcher-stats.json"
|
|
|
|
# Cores para output
|
|
$Colors = @{
|
|
Red = "Red"
|
|
Green = "Green"
|
|
Yellow = "Yellow"
|
|
Blue = "Blue"
|
|
Cyan = "Cyan"
|
|
Magenta = "Magenta"
|
|
}
|
|
|
|
# Função de logging
|
|
function Write-Log {
|
|
param(
|
|
[string]$Message,
|
|
[string]$Level = "info",
|
|
[string]$Color = "White"
|
|
)
|
|
|
|
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
|
$logEntry = "[$timestamp] [$Level] $Message"
|
|
|
|
# Criar diretório de logs se não existir
|
|
$logDir = Split-Path $script:LogFile -Parent
|
|
if (-not (Test-Path $logDir)) {
|
|
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
|
|
}
|
|
|
|
# Escrever no arquivo de log
|
|
Add-Content -Path $script:LogFile -Value $logEntry
|
|
|
|
# Exibir no console se verbose ou nível apropriado
|
|
if ($Verbose -or $Level -in @("warn", "error")) {
|
|
Write-Host $logEntry -ForegroundColor $Color
|
|
}
|
|
}
|
|
|
|
# Carregar configuração
|
|
function Load-Config {
|
|
if (Test-Path $ConfigFile) {
|
|
try {
|
|
$script:Config = Get-Content $ConfigFile | ConvertFrom-Json
|
|
Write-Log "Configuração carregada de $ConfigFile" "info" "Green"
|
|
} catch {
|
|
Write-Log "Erro ao carregar configuração: $($_.Exception.Message)" "error" "Red"
|
|
return $false
|
|
}
|
|
} else {
|
|
Write-Log "Arquivo de configuração não encontrado: $ConfigFile" "warn" "Yellow"
|
|
# Criar configuração padrão
|
|
Create-DefaultConfig
|
|
}
|
|
return $true
|
|
}
|
|
|
|
# Criar configuração padrão
|
|
function Create-DefaultConfig {
|
|
$defaultConfig = @{
|
|
repository = @{
|
|
remote_url = "https://github.com/Reifonas/TS_RDO.git"
|
|
branch = "main"
|
|
auto_push = $true
|
|
}
|
|
monitoring = @{
|
|
enabled = $true
|
|
interval_seconds = 5
|
|
watch_patterns = @("*.ts", "*.tsx", "*.js", "*.jsx", "*.json", "*.md", "*.yml", "*.yaml")
|
|
ignore_patterns = @("node_modules/**", "dist/**", "build/**", ".git/**", "logs/**")
|
|
directories = @("src", "public", ".github", "scripts")
|
|
}
|
|
actions = @{
|
|
on_change = @("sync")
|
|
on_error = @("log", "notify")
|
|
debounce_ms = 2000
|
|
}
|
|
|
|
notifications = @{
|
|
enabled = $true
|
|
methods = @("console", "file")
|
|
}
|
|
}
|
|
|
|
$defaultConfig | ConvertTo-Json -Depth 4 | Set-Content $ConfigFile
|
|
$script:Config = $defaultConfig
|
|
Write-Log "Configuração padrão criada em $ConfigFile" "info" "Cyan"
|
|
}
|
|
|
|
# Inicializar sistema de monitoramento
|
|
function Initialize-Watcher {
|
|
Write-Log "Inicializando sistema de monitoramento..." "info" "Yellow"
|
|
|
|
# Verificar se Git está disponível
|
|
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
|
|
Write-Log "Git não encontrado no PATH" "error" "Red"
|
|
return $false
|
|
}
|
|
|
|
# Verificar se estamos em um repositório Git
|
|
if (-not (Test-Path ".git")) {
|
|
Write-Log "Não é um repositório Git válido" "error" "Red"
|
|
return $false
|
|
}
|
|
|
|
# Criar diretórios necessários
|
|
$dirs = @("logs", "temp")
|
|
foreach ($dir in $dirs) {
|
|
if (-not (Test-Path $dir)) {
|
|
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
|
Write-Log "Diretório criado: $dir" "info" "Green"
|
|
}
|
|
}
|
|
|
|
# Inicializar estatísticas
|
|
Initialize-Stats
|
|
|
|
$script:IsRunning = $true
|
|
Write-Log "Sistema de monitoramento inicializado" "info" "Green"
|
|
return $true
|
|
}
|
|
|
|
# Inicializar estatísticas
|
|
function Initialize-Stats {
|
|
if (-not (Test-Path $script:StatsFile)) {
|
|
$stats = @{
|
|
start_time = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
|
|
total_changes = 0
|
|
total_syncs = 0
|
|
|
|
last_change = $null
|
|
last_sync = $null
|
|
errors = 0
|
|
uptime_seconds = 0
|
|
}
|
|
|
|
$stats | ConvertTo-Json -Depth 2 | Set-Content $script:StatsFile
|
|
}
|
|
}
|
|
|
|
# Atualizar estatísticas
|
|
function Update-Stats {
|
|
param(
|
|
[string]$Event,
|
|
[hashtable]$Data = @{}
|
|
)
|
|
|
|
try {
|
|
$stats = Get-Content $script:StatsFile | ConvertFrom-Json
|
|
|
|
switch ($Event) {
|
|
"change" {
|
|
$stats.total_changes++
|
|
$stats.last_change = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
|
}
|
|
"sync" {
|
|
$stats.total_syncs++
|
|
$stats.last_sync = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
|
}
|
|
|
|
"error" {
|
|
$stats.errors++
|
|
}
|
|
}
|
|
|
|
# Calcular uptime
|
|
$startTime = [DateTime]::ParseExact($stats.start_time, "yyyy-MM-dd HH:mm:ss", $null)
|
|
$stats.uptime_seconds = [int]((Get-Date) - $startTime).TotalSeconds
|
|
|
|
$stats | ConvertTo-Json -Depth 2 | Set-Content $script:StatsFile
|
|
} catch {
|
|
Write-Log "Erro ao atualizar estatísticas: $($_.Exception.Message)" "error" "Red"
|
|
}
|
|
}
|
|
|
|
# Criar FileSystemWatcher
|
|
function Start-FileWatcher {
|
|
param([string]$Path, [string[]]$Patterns)
|
|
|
|
try {
|
|
$watcher = New-Object System.IO.FileSystemWatcher
|
|
$watcher.Path = $Path
|
|
$watcher.IncludeSubdirectories = $true
|
|
$watcher.EnableRaisingEvents = $true
|
|
|
|
# Eventos a monitorar
|
|
$watcher.NotifyFilter = [System.IO.NotifyFilters]::LastWrite -bor
|
|
[System.IO.NotifyFilters]::FileName -bor
|
|
[System.IO.NotifyFilters]::DirectoryName
|
|
|
|
# Registrar eventos
|
|
$action = {
|
|
$path = $Event.SourceEventArgs.FullPath
|
|
$changeType = $Event.SourceEventArgs.ChangeType
|
|
$name = $Event.SourceEventArgs.Name
|
|
|
|
# Verificar se o arquivo deve ser ignorado
|
|
if (Should-IgnoreFile -FilePath $name) {
|
|
return
|
|
}
|
|
|
|
Write-Log "Mudança detectada: $changeType - $name" "info" "Cyan"
|
|
|
|
# Atualizar estatísticas
|
|
Update-Stats -Event "change"
|
|
|
|
# Executar ações configuradas
|
|
Invoke-ChangeActions -FilePath $path -ChangeType $changeType
|
|
}
|
|
|
|
Register-ObjectEvent -InputObject $watcher -EventName "Changed" -Action $action
|
|
Register-ObjectEvent -InputObject $watcher -EventName "Created" -Action $action
|
|
Register-ObjectEvent -InputObject $watcher -EventName "Deleted" -Action $action
|
|
Register-ObjectEvent -InputObject $watcher -EventName "Renamed" -Action $action
|
|
|
|
$script:WatcherJobs += $watcher
|
|
Write-Log "FileWatcher iniciado para: $Path" "info" "Green"
|
|
|
|
return $watcher
|
|
} catch {
|
|
Write-Log "Erro ao iniciar FileWatcher: $($_.Exception.Message)" "error" "Red"
|
|
return $null
|
|
}
|
|
}
|
|
|
|
# Verificar se arquivo deve ser ignorado
|
|
function Should-IgnoreFile {
|
|
param([string]$FilePath)
|
|
|
|
$ignorePatterns = $script:Config.monitoring.ignore_patterns
|
|
|
|
foreach ($pattern in $ignorePatterns) {
|
|
if ($FilePath -like $pattern) {
|
|
return $true
|
|
}
|
|
}
|
|
|
|
return $false
|
|
}
|
|
|
|
# Executar ações quando mudanças são detectadas
|
|
function Invoke-ChangeActions {
|
|
param(
|
|
[string]$FilePath,
|
|
[string]$ChangeType
|
|
)
|
|
|
|
$actions = $script:Config.actions.on_change
|
|
|
|
foreach ($actionType in $actions) {
|
|
switch ($actionType) {
|
|
|
|
"sync" {
|
|
if ($script:Config.repository.auto_push) {
|
|
Start-Sync
|
|
}
|
|
}
|
|
"notify" {
|
|
Send-Notification -Message "Arquivo modificado: $FilePath" -Type $ChangeType
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
# Sincronizar com repositório
|
|
function Start-Sync {
|
|
# Debounce - evitar múltiplas sincronizações muito próximas
|
|
$timeSinceLastSync = (Get-Date) - $script:LastSync
|
|
if ($timeSinceLastSync.TotalMilliseconds -lt $script:Config.actions.debounce_ms) {
|
|
Write-Log "Sincronização ignorada (debounce)" "debug" "Gray"
|
|
return
|
|
}
|
|
|
|
try {
|
|
Write-Log "Iniciando sincronização..." "info" "Yellow"
|
|
|
|
# Verificar se há mudanças
|
|
$status = git status --porcelain
|
|
if (-not $status) {
|
|
Write-Log "Nenhuma mudança para sincronizar" "info" "Gray"
|
|
return
|
|
}
|
|
|
|
# Executar script de sincronização
|
|
$syncScript = "scripts\auto-sync-github.ps1"
|
|
if (Test-Path $syncScript) {
|
|
& $syncScript -AutoCommit -Push
|
|
Write-Log "Sincronização concluída" "info" "Green"
|
|
|
|
Update-Stats -Event "sync"
|
|
$script:LastSync = Get-Date
|
|
} else {
|
|
Write-Log "Script de sincronização não encontrado: $syncScript" "warn" "Yellow"
|
|
}
|
|
} catch {
|
|
Write-Log "Erro na sincronização: $($_.Exception.Message)" "error" "Red"
|
|
Update-Stats -Event "error"
|
|
}
|
|
}
|
|
|
|
# Enviar notificação
|
|
function Send-Notification {
|
|
param(
|
|
[string]$Message,
|
|
[string]$Type = "info"
|
|
)
|
|
|
|
if (-not $script:Config.notifications.enabled) {
|
|
return
|
|
}
|
|
|
|
$methods = $script:Config.notifications.methods
|
|
|
|
foreach ($method in $methods) {
|
|
switch ($method) {
|
|
"console" {
|
|
$color = switch ($Type) {
|
|
"error" { "Red" }
|
|
"warn" { "Yellow" }
|
|
"success" { "Green" }
|
|
default { "Cyan" }
|
|
}
|
|
Write-Host "🔔 $Message" -ForegroundColor $color
|
|
}
|
|
"file" {
|
|
Write-Log "NOTIFICATION: $Message" "info" "Cyan"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Parar monitoramento
|
|
function Stop-Watcher {
|
|
Write-Log "Parando sistema de monitoramento..." "info" "Yellow"
|
|
|
|
foreach ($watcher in $script:WatcherJobs) {
|
|
if ($watcher) {
|
|
$watcher.EnableRaisingEvents = $false
|
|
$watcher.Dispose()
|
|
}
|
|
}
|
|
|
|
# Limpar jobs de eventos
|
|
Get-EventSubscriber | Unregister-Event
|
|
|
|
$script:IsRunning = $false
|
|
Write-Log "Sistema de monitoramento parado" "info" "Green"
|
|
}
|
|
|
|
# Exibir estatísticas
|
|
function Show-Stats {
|
|
if (Test-Path $script:StatsFile) {
|
|
$stats = Get-Content $script:StatsFile | ConvertFrom-Json
|
|
|
|
Write-Host ""
|
|
Write-Host "📊 Estatísticas do File Watcher" -ForegroundColor Cyan
|
|
Write-Host "================================" -ForegroundColor Cyan
|
|
Write-Host "Início: $($stats.start_time)" -ForegroundColor White
|
|
Write-Host "Uptime: $([math]::Round($stats.uptime_seconds / 3600, 2)) horas" -ForegroundColor White
|
|
Write-Host "Total de mudanças: $($stats.total_changes)" -ForegroundColor Green
|
|
Write-Host "Total de sincronizações: $($stats.total_syncs)" -ForegroundColor Green
|
|
|
|
Write-Host "Erros: $($stats.errors)" -ForegroundColor $(if ($stats.errors -gt 0) { "Red" } else { "Green" })
|
|
|
|
if ($stats.last_change) {
|
|
Write-Host "Última mudança: $($stats.last_change)" -ForegroundColor Yellow
|
|
}
|
|
if ($stats.last_sync) {
|
|
Write-Host "Última sincronização: $($stats.last_sync)" -ForegroundColor Yellow
|
|
}
|
|
Write-Host ""
|
|
}
|
|
}
|
|
|
|
# Função principal
|
|
function Main {
|
|
Write-Host "🔍 File Watcher para RDO-C" -ForegroundColor Cyan
|
|
Write-Host "==========================" -ForegroundColor Cyan
|
|
|
|
# Carregar configuração
|
|
if (-not (Load-Config)) {
|
|
Write-Host "❌ Falha ao carregar configuração" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
# Inicializar sistema
|
|
if (-not (Initialize-Watcher)) {
|
|
Write-Host "❌ Falha ao inicializar watcher" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
# Iniciar monitoramento para cada diretório configurado
|
|
foreach ($dir in $script:Config.monitoring.directories) {
|
|
if (Test-Path $dir) {
|
|
$watcher = Start-FileWatcher -Path (Resolve-Path $dir) -Patterns $script:Config.monitoring.watch_patterns
|
|
if ($watcher) {
|
|
Write-Host "✅ Monitorando: $dir" -ForegroundColor Green
|
|
}
|
|
} else {
|
|
Write-Log "Diretório não encontrado: $dir" "warn" "Yellow"
|
|
}
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "🚀 File Watcher ativo!" -ForegroundColor Green
|
|
Write-Host "Pressione Ctrl+C para parar ou 's' para estatísticas" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
|
|
# Loop principal
|
|
try {
|
|
while ($script:IsRunning) {
|
|
if ([Console]::KeyAvailable) {
|
|
$key = [Console]::ReadKey($true)
|
|
switch ($key.KeyChar) {
|
|
's' { Show-Stats }
|
|
'q' {
|
|
Write-Host "Parando..." -ForegroundColor Yellow
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
Start-Sleep -Seconds $Interval
|
|
}
|
|
} catch {
|
|
Write-Log "Erro no loop principal: $($_.Exception.Message)" "error" "Red"
|
|
} finally {
|
|
Stop-Watcher
|
|
}
|
|
}
|
|
|
|
# Tratamento de Ctrl+C
|
|
$null = Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action {
|
|
Stop-Watcher
|
|
}
|
|
|
|
# Executar função principal
|
|
if ($MyInvocation.InvocationName -ne '.') {
|
|
Main
|
|
} |