First commit - backup RDOC

This commit is contained in:
2026-02-20 07:20:32 -03:00
commit b7415f0586
259 changed files with 51707 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
#!/bin/bash
# Script para aplicar migrations usando Supabase CLI
echo "╔═══════════════════════════════════════════════════════════════╗"
echo "║ ║"
echo "║ 🚀 APLICANDO MIGRATIONS - SUPABASE RDO ║"
echo "║ ║"
echo "╚═══════════════════════════════════════════════════════════════╝"
echo ""
# Project ID
PROJECT_ID="ympbgdymeesivfajmgat"
echo "📍 Projeto: $PROJECT_ID"
echo ""
# 1. Linkar projeto
echo "1⃣ Linkando projeto Supabase..."
supabase link --project-ref $PROJECT_ID
if [ $? -ne 0 ]; then
echo "❌ Erro ao linkar projeto"
exit 1
fi
echo "✅ Projeto linkado com sucesso!"
echo ""
# 2. Aplicar migrations
echo "2⃣ Aplicando migrations..."
supabase db push
if [ $? -ne 0 ]; then
echo "❌ Erro ao aplicar migrations"
exit 1
fi
echo "✅ Migrations aplicadas com sucesso!"
echo ""
# 3. Verificar
echo "3⃣ Verificando..."
node check-supabase-status.js
echo ""
echo "╔═══════════════════════════════════════════════════════════════╗"
echo "║ ║"
echo "║ ✅ TUDO PRONTO! ║"
echo "║ ║"
echo "║ Próximo passo: npm run dev ║"
echo "║ ║"
echo "╚═══════════════════════════════════════════════════════════════╝"

102
scripts/apply-migrations.js Normal file
View File

@@ -0,0 +1,102 @@
import { createClient } from '@supabase/supabase-js';
import { readFileSync, readdirSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Ler .env
const envContent = readFileSync(join(__dirname, '..', '.env'), 'utf-8');
const envVars = {};
envContent.split('\n').forEach(line => {
const match = line.match(/^([^#=]+)=(.*)$/);
if (match) {
envVars[match[1].trim()] = match[2].trim();
}
});
const supabaseUrl = envVars.VITE_SUPABASE_URL;
const supabaseKey = envVars.VITE_SUPABASE_ANON_KEY;
// IMPORTANTE: Para aplicar migrations, você precisa da SERVICE_ROLE_KEY
// Descomente a linha abaixo e adicione a chave no .env
// const supabaseServiceKey = envVars.SUPABASE_SERVICE_ROLE_KEY;
if (!supabaseUrl || !supabaseKey) {
console.error('❌ Credenciais do Supabase não encontradas no .env');
process.exit(1);
}
console.log('⚠️ ATENÇÃO: Este script precisa da SERVICE_ROLE_KEY para funcionar!');
console.log('📝 Por segurança, recomendamos usar o Supabase CLI ou o Dashboard.\n');
console.log('Para usar este script:');
console.log('1. Descomente a linha SUPABASE_SERVICE_ROLE_KEY no .env');
console.log('2. Adicione sua service role key (encontre em: Dashboard > Settings > API)');
console.log('3. Execute novamente\n');
// Se você quiser usar este script, descomente o código abaixo:
/*
const supabase = createClient(supabaseUrl, supabaseServiceKey);
async function applyMigrations() {
console.log('🚀 Iniciando aplicação de migrations...\n');
const migrationsDir = join(__dirname, 'supabase', 'migrations');
const files = readdirSync(migrationsDir)
.filter(f => f.endsWith('.sql'))
.sort();
console.log(`📁 Encontradas ${files.length} migrations:\n`);
files.forEach(f => console.log(` - ${f}`));
console.log('');
for (const file of files) {
console.log(`⏳ Aplicando: ${file}...`);
const sql = readFileSync(join(migrationsDir, file), 'utf-8');
try {
const { data, error } = await supabase.rpc('exec_sql', { sql_query: sql });
if (error) {
console.error(`❌ Erro ao aplicar ${file}:`);
console.error(error);
process.exit(1);
}
console.log(`✅ ${file} aplicada com sucesso!\n`);
} catch (err) {
console.error(`❌ Erro ao aplicar ${file}:`);
console.error(err.message);
process.exit(1);
}
}
console.log('🎉 Todas as migrations foram aplicadas com sucesso!');
console.log('\n📊 Verificando estrutura criada...\n');
// Verificar tabelas criadas
const { data: tables, error: tablesError } = await supabase
.from('information_schema.tables')
.select('table_name')
.eq('table_schema', 'public')
.order('table_name');
if (!tablesError && tables) {
console.log('✅ Tabelas criadas:');
tables.forEach(t => console.log(` - ${t.table_name}`));
}
}
applyMigrations().catch(console.error);
*/
console.log('💡 RECOMENDAÇÃO: Use o Supabase CLI para aplicar as migrations:');
console.log('');
console.log(' 1. supabase link --project-ref bbyzrywmgjiufqtnkslu');
console.log(' 2. supabase db push');
console.log('');
console.log('Ou use o Dashboard do Supabase (SQL Editor) para executar manualmente.');
console.log('');
console.log('Veja instruções completas em: INSTRUCOES_DEPLOY_SUPABASE.md');

View File

@@ -0,0 +1,48 @@
#!/usr/bin/env pwsh
# Script para aplicar migrations usando Supabase CLI (Windows)
Write-Output "`n╔═══════════════════════════════════════════════════════════════╗"
Write-Output "║ ║"
Write-Output "║ 🚀 APLICANDO MIGRATIONS - SUPABASE RDO ║"
Write-Output "║ ║"
Write-Output "╚═══════════════════════════════════════════════════════════════╝`n"
# Project ID
$PROJECT_ID = "mnwrnblzabxgqtgjwxgl"
Write-Output "📍 Projeto: $PROJECT_ID`n"
# 1. Linkar projeto
Write-Output "1⃣ Linkando projeto Supabase..."
supabase link --project-ref $PROJECT_ID
if ($LASTEXITCODE -ne 0) {
Write-Output "❌ Erro ao linkar projeto"
exit 1
}
Write-Output "✅ Projeto linkado com sucesso!`n"
# 2. Aplicar migrations
Write-Output "2⃣ Aplicando migrations..."
supabase db push
if ($LASTEXITCODE -ne 0) {
Write-Output "❌ Erro ao aplicar migrations"
exit 1
}
Write-Output "✅ Migrations aplicadas com sucesso!`n"
# 3. Verificar
Write-Output "3⃣ Verificando..."
node check-supabase-status.js
Write-Output "`n╔═══════════════════════════════════════════════════════════════╗"
Write-Output "║ ║"
Write-Output "║ ✅ TUDO PRONTO! ║"
Write-Output "║ ║"
Write-Output "║ Próximo passo: npm run dev ║"
Write-Output "║ ║"
Write-Output "╚═══════════════════════════════════════════════════════════════╝`n"

View File

@@ -0,0 +1,246 @@
# Script de Sincronização Automática com GitHub
# Autor: Sistema RDO
# Versão: 2.0
# Descrição: Script avançado para sincronização automática com repositório GitHub
param(
[string]$RemoteRepo = "https://github.com/Reifonas/TS_RDO.git",
[string]$Branch = "main",
[int]$WatchInterval = 30,
[switch]$ContinuousMode,
[switch]$Verbose
)
# Configurações
$ProjectPath = Split-Path -Parent $PSScriptRoot
$LogPath = Join-Path $ProjectPath "logs"
$ConfigPath = Join-Path $ProjectPath "auto-sync-config.json"
# Criar diretórios necessários
if (!(Test-Path $LogPath)) { New-Item -ItemType Directory -Path $LogPath -Force }
# Função de logging
function Write-Log {
param([string]$Message, [string]$Level = "INFO")
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "[$timestamp] [$Level] $Message"
$logFile = Join-Path $LogPath "auto-sync-$(Get-Date -Format 'yyyy-MM-dd').log"
Write-Host $logMessage -ForegroundColor $(switch($Level) {
"ERROR" { "Red" }
"WARN" { "Yellow" }
"SUCCESS" { "Green" }
default { "White" }
})
Add-Content -Path $logFile -Value $logMessage
}
# Função para verificar se é um repositório Git
function Test-GitRepository {
try {
$null = git rev-parse --git-dir 2>$null
return $true
} catch {
return $false
}
}
# Função para inicializar repositório Git
function Initialize-GitRepository {
Write-Log "Inicializando repositório Git..." "INFO"
try {
# Inicializar repositório
git init
# Configurar remote
git remote add origin $RemoteRepo
# Configurar branch principal
git branch -M $Branch
Write-Log "Repositório Git inicializado com sucesso" "SUCCESS"
return $true
} catch {
Write-Log "Erro ao inicializar repositório: $($_.Exception.Message)" "ERROR"
return $false
}
}
# Função para verificar mudanças
function Test-HasChanges {
try {
$status = git status --porcelain 2>$null
return ![string]::IsNullOrEmpty($status)
} catch {
Write-Log "Erro ao verificar mudanças: $($_.Exception.Message)" "ERROR"
return $false
}
}
# Função para gerar mensagem de commit automática
function Get-AutoCommitMessage {
$changedFiles = git diff --name-only HEAD 2>$null
$newFiles = git ls-files --others --exclude-standard 2>$null
$deletedFiles = git diff --name-only --diff-filter=D HEAD 2>$null
$changes = @()
if ($changedFiles) { $changes += "Modified: $($changedFiles.Count) files" }
if ($newFiles) { $changes += "Added: $($newFiles.Count) files" }
if ($deletedFiles) { $changes += "Deleted: $($deletedFiles.Count) files" }
$message = "Auto-sync: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
if ($changes.Count -gt 0) {
$message += " - " + ($changes -join ", ")
}
return $message
}
# Função principal de sincronização
function Sync-WithGitHub {
param([bool]$CreateBackup = $false)
Write-Log "Iniciando sincronização com GitHub..." "INFO"
# Verificar se é repositório Git
if (!(Test-GitRepository)) {
Write-Log "Não é um repositório Git. Inicializando..." "WARN"
if (!(Initialize-GitRepository)) {
return $false
}
}
# Verificar mudanças
if (!(Test-HasChanges)) {
Write-Log "Nenhuma mudança detectada" "INFO"
return $true
}
try {
# Fazer pull das mudanças remotas
Write-Log "Fazendo pull das mudanças remotas..." "INFO"
git pull origin $Branch --rebase 2>$null
# Adicionar todos os arquivos
Write-Log "Adicionando arquivos..." "INFO"
git add .
# Commit com mensagem automática
$commitMessage = Get-AutoCommitMessage
Write-Log "Fazendo commit: $commitMessage" "INFO"
git commit -m $commitMessage
# Push para o repositório remoto
Write-Log "Enviando para GitHub..." "INFO"
git push origin $Branch
Write-Log "Sincronização concluída com sucesso!" "SUCCESS"
return $true
} catch {
Write-Log "Erro durante sincronização: $($_.Exception.Message)" "ERROR"
# Log do erro para análise
if ($false) {
Write-Log "Tentando rollback..." "WARN"
try {
git reset --hard HEAD~1 2>$null
Write-Log "Rollback realizado" "SUCCESS"
} catch {
Write-Log "Falha no rollback: $($_.Exception.Message)" "ERROR"
}
}
return $false
}
}
# Função de monitoramento contínuo
function Start-ContinuousSync {
Write-Log "Iniciando monitoramento contínuo (intervalo: $WatchInterval segundos)" "INFO"
Write-Log "Pressione Ctrl+C para parar" "INFO"
$lastSyncTime = Get-Date
try {
while ($true) {
$currentTime = Get-Date
# Verificar se passou o intervalo
if (($currentTime - $lastSyncTime).TotalSeconds -ge $WatchInterval) {
if (Test-HasChanges) {
Write-Log "Mudanças detectadas. Iniciando sincronização..." "INFO"
if (Sync-WithGitHub) {
$lastSyncTime = $currentTime
}
} elseif ($Verbose) {
Write-Log "Verificação realizada - sem mudanças" "INFO"
}
$lastSyncTime = $currentTime
}
Start-Sleep -Seconds 5
}
} catch {
Write-Log "Monitoramento interrompido: $($_.Exception.Message)" "WARN"
}
}
# Função para limpar logs antigos
function Clear-OldLogs {
param([int]$DaysToKeep = 7)
try {
$cutoffDate = (Get-Date).AddDays(-$DaysToKeep)
$oldLogs = Get-ChildItem -Path $LogPath -Filter "*.log" | Where-Object { $_.LastWriteTime -lt $cutoffDate }
foreach ($log in $oldLogs) {
Remove-Item $log.FullName -Force
Write-Log "Log antigo removido: $($log.Name)" "INFO"
}
} catch {
Write-Log "Erro ao limpar logs antigos: $($_.Exception.Message)" "ERROR"
}
}
# Execução principal
function Main {
Write-Log "=== Iniciando Auto-Sync GitHub ==="
Write-Log "Repositório: $RemoteRepo"
Write-Log "Branch: $Branch"
Write-Log "Diretório: $ProjectPath"
# Mudar para o diretório do projeto
Set-Location $ProjectPath
# Limpar arquivos antigos
Clear-OldLogs
if ($ContinuousMode) {
Start-ContinuousSync
} else {
$result = Sync-WithGitHub
if ($result) {
Write-Log "Sincronização única concluída com sucesso" "SUCCESS"
exit 0
} else {
Write-Log "Falha na sincronização" "ERROR"
exit 1
}
}
}
# Executar script principal
Main

358
scripts/auto-sync.js Normal file
View File

@@ -0,0 +1,358 @@
#!/usr/bin/env node
/**
* Auto-Sync Script para RDO Project
* Monitora mudanças em tempo real e sincroniza com GitHub a cada 15 segundos
* Autor: Sistema RDO
* Versão: 3.0
*/
import chokidar from 'chokidar';
import { exec } from 'child_process';
import fs from 'fs';
import path from 'path';
import util from 'util';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const execAsync = util.promisify(exec);
// Configurações
const CONFIG = {
REMOTE_REPO: 'https://github.com/Reifonas/TS_RDO.git',
BRANCH: 'main',
SYNC_INTERVAL: 15000, // 15 segundos
PROJECT_PATH: process.cwd(),
LOG_PATH: path.join(process.cwd(), 'logs'),
WATCH_PATTERNS: [
'**/*',
'!node_modules/**',
'!.git/**',
'!dist/**',
'!build/**',
'!logs/**',
'!*.log',
'!.env.local'
]
};
// Estado do aplicativo
let hasChanges = false;
let isProcessing = false;
let lastSyncTime = 0;
let changeQueue = new Set();
// Criar diretório de logs se não existir
if (!fs.existsSync(CONFIG.LOG_PATH)) {
fs.mkdirSync(CONFIG.LOG_PATH, { recursive: true });
}
/**
* Função de logging com timestamp
*/
function log(message, level = 'INFO') {
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19);
const logMessage = `[${timestamp}] [${level}] ${message}`;
// Console com cores
const colors = {
ERROR: '\x1b[31m', // Vermelho
WARN: '\x1b[33m', // Amarelo
SUCCESS: '\x1b[32m', // Verde
INFO: '\x1b[36m', // Ciano
RESET: '\x1b[0m' // Reset
};
console.log(`${colors[level] || colors.INFO}${logMessage}${colors.RESET}`);
// Salvar em arquivo
const logFile = path.join(CONFIG.LOG_PATH, `auto-sync-${new Date().toISOString().split('T')[0]}.log`);
fs.appendFileSync(logFile, logMessage + '\n');
}
/**
* Verificar se é um repositório Git
*/
async function isGitRepository() {
try {
await execAsync('git rev-parse --git-dir');
return true;
} catch {
return false;
}
}
/**
* Inicializar repositório Git
*/
async function initializeGitRepository() {
try {
log('Inicializando repositório Git...', 'INFO');
await execAsync('git init');
await execAsync(`git remote add origin ${CONFIG.REMOTE_REPO}`);
await execAsync(`git branch -M ${CONFIG.BRANCH}`);
log('Repositório Git inicializado com sucesso', 'SUCCESS');
return true;
} catch (error) {
log(`Erro ao inicializar repositório: ${error.message}`, 'ERROR');
return false;
}
}
/**
* Verificar se há mudanças no repositório
*/
async function checkForChanges() {
try {
const { stdout } = await execAsync('git status --porcelain');
return stdout.trim().length > 0;
} catch (error) {
log(`Erro ao verificar mudanças: ${error.message}`, 'ERROR');
return false;
}
}
/**
* Gerar mensagem de commit automática
*/
async function generateCommitMessage() {
try {
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19);
// Obter estatísticas das mudanças
const { stdout: modifiedFiles } = await execAsync('git diff --name-only HEAD').catch(() => ({ stdout: '' }));
const { stdout: newFiles } = await execAsync('git ls-files --others --exclude-standard').catch(() => ({ stdout: '' }));
const { stdout: deletedFiles } = await execAsync('git diff --name-only --diff-filter=D HEAD').catch(() => ({ stdout: '' }));
const changes = [];
if (modifiedFiles.trim()) changes.push(`Modified: ${modifiedFiles.trim().split('\n').length} files`);
if (newFiles.trim()) changes.push(`Added: ${newFiles.trim().split('\n').length} files`);
if (deletedFiles.trim()) changes.push(`Deleted: ${deletedFiles.trim().split('\n').length} files`);
let message = `Auto-sync: ${timestamp}`;
if (changes.length > 0) {
message += ` - ${changes.join(', ')}`;
}
return message;
} catch (error) {
return `Auto-sync: ${new Date().toISOString().replace('T', ' ').substring(0, 19)}`;
}
}
/**
* Sincronizar com GitHub
*/
async function syncWithGitHub() {
if (isProcessing) {
log('Sincronização já em andamento, aguardando...', 'WARN');
return false;
}
isProcessing = true;
try {
log('Iniciando sincronização com GitHub...', 'INFO');
// Verificar se é repositório Git
if (!(await isGitRepository())) {
log('Não é um repositório Git. Inicializando...', 'WARN');
if (!(await initializeGitRepository())) {
return false;
}
}
// Verificar mudanças
if (!(await checkForChanges())) {
log('Nenhuma mudança detectada', 'INFO');
return true;
}
// Pull das mudanças remotas (se houver)
try {
await execAsync(`git pull origin ${CONFIG.BRANCH} --rebase`);
log('Pull realizado com sucesso', 'INFO');
} catch (error) {
log('Primeira sincronização ou sem mudanças remotas', 'INFO');
}
// Adicionar arquivos
await execAsync('git add .');
log('Arquivos adicionados ao stage', 'INFO');
// Commit
const commitMessage = await generateCommitMessage();
await execAsync(`git commit -m "${commitMessage}"`);
log(`Commit realizado: ${commitMessage}`, 'INFO');
// Push
await execAsync(`git push origin ${CONFIG.BRANCH}`);
log('Push realizado com sucesso!', 'SUCCESS');
// Resetar estado
hasChanges = false;
changeQueue.clear();
lastSyncTime = Date.now();
return true;
} catch (error) {
log(`Erro durante sincronização: ${error.message}`, 'ERROR');
return false;
} finally {
isProcessing = false;
}
}
/**
* Inicializar monitoramento de arquivos
*/
function initializeWatcher() {
log('Inicializando monitoramento de arquivos...', 'INFO');
log(`Padrões monitorados: ${CONFIG.WATCH_PATTERNS.join(', ')}`, 'INFO');
const watcher = chokidar.watch(CONFIG.WATCH_PATTERNS, {
ignored: /(^|[\/\\])\../, // Ignorar arquivos ocultos
persistent: true,
ignoreInitial: true,
awaitWriteFinish: {
stabilityThreshold: 1000,
pollInterval: 100
}
});
// Eventos de mudança
watcher
.on('add', path => {
log(`Arquivo adicionado: ${path}`, 'INFO');
hasChanges = true;
changeQueue.add(path);
})
.on('change', path => {
log(`Arquivo modificado: ${path}`, 'INFO');
hasChanges = true;
changeQueue.add(path);
})
.on('unlink', path => {
log(`Arquivo removido: ${path}`, 'INFO');
hasChanges = true;
changeQueue.add(path);
})
.on('error', error => {
log(`Erro no monitoramento: ${error}`, 'ERROR');
})
.on('ready', () => {
log('Monitoramento de arquivos ativo!', 'SUCCESS');
});
return watcher;
}
/**
* Loop principal de sincronização
*/
function startSyncLoop() {
setInterval(async () => {
if (hasChanges && !isProcessing) {
const timeSinceLastSync = Date.now() - lastSyncTime;
if (timeSinceLastSync >= CONFIG.SYNC_INTERVAL) {
log(`Mudanças detectadas (${changeQueue.size} arquivos). Iniciando sincronização...`, 'INFO');
await syncWithGitHub();
} else {
const remainingTime = Math.ceil((CONFIG.SYNC_INTERVAL - timeSinceLastSync) / 1000);
log(`Aguardando ${remainingTime}s para próxima sincronização...`, 'INFO');
}
}
}, 5000); // Verificar a cada 5 segundos
}
/**
* Limpeza de logs antigos
*/
function cleanOldLogs() {
try {
const files = fs.readdirSync(CONFIG.LOG_PATH);
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - 7); // Manter logs por 7 dias
files.forEach(file => {
if (file.endsWith('.log')) {
const filePath = path.join(CONFIG.LOG_PATH, file);
const stats = fs.statSync(filePath);
if (stats.mtime < cutoffDate) {
fs.unlinkSync(filePath);
log(`Log antigo removido: ${file}`, 'INFO');
}
}
});
} catch (error) {
log(`Erro ao limpar logs antigos: ${error.message}`, 'ERROR');
}
}
/**
* Tratamento de sinais do sistema
*/
function setupSignalHandlers() {
process.on('SIGINT', () => {
log('Recebido SIGINT. Finalizando...', 'WARN');
process.exit(0);
});
process.on('SIGTERM', () => {
log('Recebido SIGTERM. Finalizando...', 'WARN');
process.exit(0);
});
process.on('uncaughtException', (error) => {
log(`Erro não capturado: ${error.message}`, 'ERROR');
process.exit(1);
});
}
/**
* Função principal
*/
async function main() {
log('=== Auto-Sync RDO v3.0 ===', 'SUCCESS');
log(`Repositório: ${CONFIG.REMOTE_REPO}`, 'INFO');
log(`Branch: ${CONFIG.BRANCH}`, 'INFO');
log(`Intervalo de sincronização: ${CONFIG.SYNC_INTERVAL / 1000}s`, 'INFO');
log(`Diretório: ${CONFIG.PROJECT_PATH}`, 'INFO');
// Configurar tratamento de sinais
setupSignalHandlers();
// Limpar logs antigos
cleanOldLogs();
// Mudar para o diretório do projeto
process.chdir(CONFIG.PROJECT_PATH);
// Inicializar monitoramento
const watcher = initializeWatcher();
// Iniciar loop de sincronização
startSyncLoop();
log('Sistema de auto-sync ativo! Pressione Ctrl+C para parar.', 'SUCCESS');
// Manter o processo ativo
process.stdin.resume();
}
// Executar automaticamente
main().catch(error => {
console.error(`Erro fatal: ${error.message}`);
process.exit(1);
});
export { main, syncWithGitHub, log };

178
scripts/auto-syncRDO.ps1 Normal file
View File

@@ -0,0 +1,178 @@
# Parâmetros de linha de comando
param(
[switch]$Once,
[int]$Interval = 15,
[string]$BranchName = "main"
)
# Configurações
$PossiblePaths = @(
"c:\Users\Administrator\Documents\GitHub\RDO-D",
"c:\Users\Marcos\Documents\GitHub\RDO-D"
)
$Branch = $BranchName
$IntervalSeconds = $Interval
$RunOnce = $Once.IsPresent
$RemoteUrl = "https://github.com/Reifonas/RDO-D.git"
# Função para verificar se é um repositório Git válido
function Test-GitRepository($path) {
if (-not (Test-Path $path)) { return $false }
if (-not (Test-Path (Join-Path $path ".git"))) { return $false }
$currentDir = Get-Location
try {
Set-Location $path
$gitStatus = git status 2>&1
return $LASTEXITCODE -eq 0
} catch {
return $false
} finally {
Set-Location $currentDir
}
}
# Detecta automaticamente o caminho correto
$RepoPath = $null
foreach ($path in $PossiblePaths) {
if (Test-GitRepository $path) {
$RepoPath = $path
Write-Host "Usando repositório em: $RepoPath" -ForegroundColor Green
break
}
}
if (-not $RepoPath) {
Write-Host "ERRO: Nenhum repositório Git válido foi encontrado:" -ForegroundColor Red
foreach ($path in $PossiblePaths) {
$exists = Test-Path $path
$isGit = if ($exists) { Test-Path (Join-Path $path ".git") } else { $false }
Write-Host " - $path (Existe: $exists, Git: $isGit)" -ForegroundColor Yellow
}
Write-Host "Verifique se o repositório existe e é um repositório Git válido." -ForegroundColor Red
exit 1
}
# Função para verificar e configurar o remote
function Ensure-RemoteConfiguration {
Write-Host "Verificando configuração do remote..." -ForegroundColor Cyan
$currentRemote = git -C $RepoPath remote get-url origin 2>&1
if ($LASTEXITCODE -eq 0 -and $currentRemote -ne $RemoteUrl) {
Write-Host "Atualizando remote de '$currentRemote' para '$RemoteUrl'" -ForegroundColor Yellow
git -C $RepoPath remote set-url origin $RemoteUrl
if ($LASTEXITCODE -eq 0) {
Write-Host "Remote atualizado com sucesso!" -ForegroundColor Green
} else {
Write-Host "Erro ao atualizar remote." -ForegroundColor Red
return $false
}
} elseif ($LASTEXITCODE -ne 0) {
Write-Host "Configurando remote para '$RemoteUrl'" -ForegroundColor Yellow
git -C $RepoPath remote add origin $RemoteUrl
if ($LASTEXITCODE -eq 0) {
Write-Host "Remote configurado com sucesso!" -ForegroundColor Green
} else {
Write-Host "Erro ao configurar remote." -ForegroundColor Red
return $false
}
} else {
Write-Host "Remote já está configurado corretamente: $RemoteUrl" -ForegroundColor Green
}
return $true
}
# Função para sincronizar o repositório
function Sync-Repository {
Write-Host "Verificando alterações no repositório..." -ForegroundColor Cyan
# Garante que o remote está configurado corretamente
if (-not (Ensure-RemoteConfiguration)) {
return $false
}
# Verifica o status do repositório
$remoteStatus = git -C $RepoPath fetch --dry-run 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Host "Aviso: Não foi possível verificar o repositório remoto." -ForegroundColor Yellow
}
$status = git -C $RepoPath status --porcelain
if ($LASTEXITCODE -ne 0) {
Write-Host "Git retornou erro. Verifique se o repositório está correto." -ForegroundColor Red
return $false
}
if (-not $status) {
Write-Host "Nenhuma alteração detectada." -ForegroundColor Gray
return $true
}
Write-Host "Alterações detectadas:" -ForegroundColor Yellow
$status | ForEach-Object { Write-Host " $_" -ForegroundColor White }
Write-Host "Comitando e enviando para $Branch..." -ForegroundColor Cyan
git -C $RepoPath add -A | Out-Null
# Evita commit vazio
$diffCached = git -C $RepoPath diff --cached --name-only
if (-not $diffCached) {
Write-Host "Nenhuma alteração para commit após staging." -ForegroundColor Gray
return $true
}
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$commitResult = git -C $RepoPath commit -m "chore: autosync from Trae $timestamp" 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Host "Erro no commit: $commitResult" -ForegroundColor Red
return $false
}
# Faz pull antes do push para evitar rejeições
Write-Host "Fazendo pull de $Branch..." -ForegroundColor Yellow
$pullResult = git -C $RepoPath pull origin $Branch 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Host "Falha no pull: $pullResult" -ForegroundColor Red
Write-Host "Tentando fazer merge automático..." -ForegroundColor Yellow
$mergeResult = git -C $RepoPath merge --no-edit origin/$Branch 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Host "Conflitos de merge detectados: $mergeResult" -ForegroundColor Red
Write-Host "Intervenção manual necessária." -ForegroundColor Red
return $false
}
}
# Agora faz o push
$pushResult = git -C $RepoPath push origin $Branch 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Host "Push realizado com sucesso para $Branch em $timestamp" -ForegroundColor Green
return $true
} else {
Write-Host "Falha no push: $pushResult" -ForegroundColor Red
Write-Host "Verifique autenticação/regras da branch." -ForegroundColor Red
return $false
}
}
# Loop principal
do {
try {
$syncResult = Sync-Repository
if (-not $syncResult) {
Write-Host "Sincronização falhou. Tentando novamente em $IntervalSeconds segundos..." -ForegroundColor Red
}
} catch {
Write-Host "Erro inesperado: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "Continuando em $IntervalSeconds segundos..." -ForegroundColor Yellow
}
if ($RunOnce) {
Write-Host "Execução única concluída." -ForegroundColor Green
break
}
Write-Host "Próxima verificação em $IntervalSeconds segundos. Pressione Ctrl+C para parar." -ForegroundColor Gray
Start-Sleep -Seconds $IntervalSeconds
} while (-not $RunOnce)
Write-Host "Script finalizado." -ForegroundColor Green

View File

@@ -0,0 +1,110 @@
import { createClient } from '@supabase/supabase-js';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Ler .env manualmente
const envContent = readFileSync(join(__dirname, '..', '.env'), 'utf-8');
const envVars = {};
envContent.split('\n').forEach(line => {
const match = line.match(/^([^#=]+)=(.*)$/);
if (match) {
envVars[match[1].trim()] = match[2].trim();
}
});
const supabaseUrl = envVars.VITE_SUPABASE_URL;
const supabaseKey = envVars.VITE_SUPABASE_ANON_KEY;
const serviceRoleKey = envVars.SUPABASE_SERVICE_ROLE_KEY;
if (!supabaseUrl || !supabaseKey) {
console.error('❌ Credenciais do Supabase não encontradas no .env');
process.exit(1);
}
const supabase = createClient(supabaseUrl, supabaseKey);
async function checkSupabaseStatus() {
console.log('🔍 Verificando status do Supabase...\n');
console.log('📍 URL:', supabaseUrl);
console.log('🔑 Anon Key:', supabaseKey.substring(0, 20) + '...\n');
try {
// 1. Testar conexão básica
console.log('1⃣ Testando conexão...');
const { data: healthCheck, error: healthError } = await supabase
.from('_supabase_health_check')
.select('*')
.limit(1);
if (healthError && healthError.code !== 'PGRST116') {
console.log('⚠️ Conexão estabelecida (tabela de health check não existe, mas isso é normal)\n');
} else {
console.log('✅ Conexão estabelecida com sucesso!\n');
}
// 2. Listar todas as tabelas existentes
console.log('2⃣ Verificando tabelas existentes...');
// Tentar listar tabelas conhecidas
const knownTables = [
'usuarios',
'organizacoes',
'obras',
'rdos',
'rdo_atividades',
'rdo_mao_obra',
'rdo_equipamentos',
'rdo_ocorrencias',
'rdo_anexos',
'tarefas',
'task_logs'
];
console.log('📋 Verificando tabelas conhecidas:\n');
const existingTables = [];
for (const table of knownTables) {
const { data, error } = await supabase
.from(table)
.select('count')
.limit(1);
if (!error) {
const { count } = await supabase
.from(table)
.select('*', { count: 'exact', head: true });
existingTables.push(table);
console.log(`${table.padEnd(25)} (${count || 0} registros)`);
} else if (error.code === '42P01') {
console.log(`${table.padEnd(25)} (não existe)`);
} else {
console.log(` ⚠️ ${table.padEnd(25)} (erro: ${error.message})`);
}
}
console.log('\n3⃣ Resumo:');
console.log(` 📊 Tabelas encontradas: ${existingTables.length}/${knownTables.length}`);
console.log(` 🗄️ Banco de dados: ${existingTables.length > 0 ? 'POPULADO' : 'VAZIO'}`);
if (existingTables.length === 0) {
console.log('\n💡 O banco está vazio. Precisamos executar as migrations!');
} else {
console.log('\n💡 O banco já tem algumas tabelas. Vamos verificar se precisa de ajustes.');
}
// 4. Verificar autenticação
console.log('\n4⃣ Verificando sistema de autenticação...');
const { data: authData, error: authError } = await supabase.auth.getSession();
console.log(' Auth configurado:', !authError ? 'SIM' : 'NÃO');
} catch (error) {
console.error('❌ Erro ao verificar Supabase:', error.message);
process.exit(1);
}
}
checkSupabaseStatus();

4
scripts/check_data.js Normal file

File diff suppressed because one or more lines are too long

57
scripts/check_database.js Normal file
View File

@@ -0,0 +1,57 @@
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = 'https://bbyzrywmgjiufqtnkslu.supabase.co';
const supabaseKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJieXpyeXdtZ2ppdWZxdG5rc2x1Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTUxNjE4MzgsImV4cCI6MjA3MDczNzgzOH0.O0Eksg4_cxKk7jbC3wYLWbwZ9FsoTzsztnPPpzGL3pE';
const supabase = createClient(supabaseUrl, supabaseKey);
async function checkData() {
console.log('Verificando dados nas tabelas principais...');
try {
// Verificar obras
const { data: obras, error: obrasError } = await supabase
.from('obras')
.select('*', { count: 'exact' });
console.log(`Obras: ${obras?.length || 0} registros`);
if (obrasError) console.error('Erro obras:', obrasError);
// Verificar usuários
const { data: usuarios, error: usuariosError } = await supabase
.from('usuarios')
.select('*', { count: 'exact' });
console.log(`Usuários: ${usuarios?.length || 0} registros`);
if (usuariosError) console.error('Erro usuários:', usuariosError);
// Verificar tipos de atividade
const { data: tiposAtividade, error: tiposError } = await supabase
.from('tipos_atividade')
.select('*', { count: 'exact' });
console.log(`Tipos de Atividade: ${tiposAtividade?.length || 0} registros`);
if (tiposError) console.error('Erro tipos atividade:', tiposError);
// Verificar condições climáticas
const { data: condicoes, error: condicoesError } = await supabase
.from('condicoes_climaticas')
.select('*', { count: 'exact' });
console.log(`Condições Climáticas: ${condicoes?.length || 0} registros`);
if (condicoesError) console.error('Erro condições:', condicoesError);
// Verificar funcionários
const { data: funcionarios, error: funcionariosError } = await supabase
.from('funcionarios')
.select('*', { count: 'exact' });
console.log(`Funcionários: ${funcionarios?.length || 0} registros`);
if (funcionariosError) console.error('Erro funcionários:', funcionariosError);
} catch (error) {
console.error('Erro geral:', error);
}
}
checkData();

View File

@@ -0,0 +1,68 @@
// Script para confirmar o email de um usuário existente
import { createClient } from '@supabase/supabase-js';
// Configuração do Supabase com service role
const supabaseUrl = 'https://bbyzrywmgjiufqtnkslu.supabase.co';
const serviceRoleKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJieXpyeXdtZ2ppdWZxdG5rc2x1Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc1NTE2MTgzOCwiZXhwIjoyMDcwNzM3ODM4fQ.TXeQHsvfiebRK2OFr0hhQIRaO7m6tPD7RwMmKvC36-g';
const supabase = createClient(supabaseUrl, serviceRoleKey, {
auth: {
autoRefreshToken: false,
persistSession: false
}
});
async function confirmUserEmail() {
console.log('✉️ Confirmando email do usuário existente...');
const email = 'teste@rdomain.com';
try {
// Primeiro, vamos listar os usuários para encontrar o ID
console.log('🔍 Buscando usuário por email...');
const { data: users, error: listError } = await supabase.auth.admin.listUsers();
if (listError) {
console.error('❌ Erro ao listar usuários:', listError.message);
return;
}
const user = users.users.find(u => u.email === email);
if (!user) {
console.error('❌ Usuário não encontrado');
return;
}
console.log('👤 Usuário encontrado:', user.id);
console.log('📧 Email atual:', user.email);
console.log('✉️ Email confirmado:', user.email_confirmed_at ? 'Sim' : 'Não');
if (user.email_confirmed_at) {
console.log('✅ Email já está confirmado!');
return;
}
// Confirmar o email do usuário
console.log('🔧 Confirmando email...');
const { data, error } = await supabase.auth.admin.updateUserById(user.id, {
email_confirm: true
});
if (error) {
console.error('❌ Erro ao confirmar email:', error.message);
console.error('📋 Detalhes:', error);
} else {
console.log('✅ Email confirmado com sucesso!');
console.log('👤 ID:', data.user?.id);
console.log('📧 Email:', data.user?.email);
console.log('✉️ Confirmado em:', data.user?.email_confirmed_at);
}
} catch (error) {
console.error('💥 Erro inesperado:', error);
}
}
confirmUserEmail();

View File

@@ -0,0 +1,51 @@
// Script para criar um usuário já confirmado usando service role
import { createClient } from '@supabase/supabase-js';
// Configuração do Supabase com service role (permite criar usuários confirmados)
const supabaseUrl = 'https://bbyzrywmgjiufqtnkslu.supabase.co';
const serviceRoleKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJieXpyeXdtZ2ppdWZxdG5rc2x1Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc1NTE2MTgzOCwiZXhwIjoyMDcwNzM3ODM4fQ.TXeQHsvfiebRK2OFr0hhQIRaO7m6tPD7RwMmKvC36-g';
const supabase = createClient(supabaseUrl, serviceRoleKey, {
auth: {
autoRefreshToken: false,
persistSession: false
}
});
async function createConfirmedUser() {
console.log('🔧 Criando usuário confirmado com service role...');
const email = 'teste@rdomain.com';
const password = 'teste123456';
try {
// Primeiro, vamos tentar deletar o usuário existente se houver
console.log('🗑️ Tentando remover usuário existente...');
// Criar usuário já confirmado
const { data, error } = await supabase.auth.admin.createUser({
email: email,
password: password,
email_confirm: true, // Confirma o email automaticamente
user_metadata: {
nome: 'Usuário Teste',
tipo: 'teste'
}
});
if (error) {
console.error('❌ Erro ao criar usuário:', error.message);
console.error('📋 Detalhes:', error);
} else {
console.log('✅ Usuário criado e confirmado com sucesso!');
console.log('👤 ID:', data.user?.id);
console.log('📧 Email:', data.user?.email);
console.log('✉️ Email confirmado:', data.user?.email_confirmed_at ? 'Sim' : 'Não');
console.log('📊 Metadata:', data.user?.user_metadata);
}
} catch (error) {
console.error('💥 Erro inesperado:', error);
}
}
createConfirmedUser();

View File

@@ -0,0 +1,147 @@
#!/usr/bin/env node
import { createClient } from '@supabase/supabase-js';
import { readFileSync, readdirSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Ler .env
const envContent = readFileSync(join(__dirname, '..', '.env'), 'utf-8');
const envVars = {};
envContent.split('\n').forEach(line => {
const match = line.match(/^([^#=]+)=(.*)$/);
if (match) {
envVars[match[1].trim()] = match[2].trim();
}
});
const supabaseUrl = envVars.VITE_SUPABASE_URL;
const serviceRoleKey = envVars.SUPABASE_SERVICE_ROLE_KEY;
if (!supabaseUrl || !serviceRoleKey) {
console.error('❌ Credenciais do Supabase não encontradas no .env');
process.exit(1);
}
console.log(`
╔═══════════════════════════════════════════════════════════════╗
║ ║
║ 🚀 APLICANDO MIGRATIONS NO SUPABASE RDO ║
║ ║
╚═══════════════════════════════════════════════════════════════╝
`);
console.log('📍 URL:', supabaseUrl);
console.log('🔑 Service Role Key:', serviceRoleKey.substring(0, 20) + '...\n');
// Criar cliente com service role key
const supabase = createClient(supabaseUrl, serviceRoleKey);
async function deployMigrations() {
try {
// 1. Testar conexão
console.log('1⃣ Testando conexão com Supabase...');
const { data: testData, error: testError } = await supabase
.from('information_schema.tables')
.select('count')
.limit(1);
if (testError && testError.code !== 'PGRST116') {
console.log('⚠️ Conexão estabelecida (resposta esperada)\n');
} else {
console.log('✅ Conexão estabelecida com sucesso!\n');
}
// 2. Listar migrations
console.log('2⃣ Lendo migrations...');
const migrationsDir = join(__dirname, '..', 'supabase', 'migrations');
const files = readdirSync(migrationsDir)
.filter(f => f.startsWith('202412') && f.endsWith('.sql'))
.sort();
console.log(`📋 Encontradas ${files.length} migrations:\n`);
files.forEach((f, i) => console.log(` ${i + 1}. ${f}`));
console.log('');
// 3. Aplicar cada migration
console.log('3⃣ Aplicando migrations...\n');
for (let i = 0; i < files.length; i++) {
const file = files[i];
console.log(`⏳ [${i + 1}/${files.length}] Aplicando: ${file}`);
const sqlPath = join(migrationsDir, file);
const sql = readFileSync(sqlPath, 'utf-8');
try {
// Executar SQL diretamente
let { error } = await supabase.rpc('exec', {
sql_query: sql
});
if (error && error.code === 'PGRST202') {
// Função exec não existe, tenta criar
error = { message: 'Function exec does not exist' };
}
if (error) {
// Tentar executar como múltiplas queries
const queries = sql
.split(';')
.map(q => q.trim())
.filter(q => q.length > 0 && !q.startsWith('--'));
for (const query of queries) {
if (query.length > 0) {
const { error: queryError } = await supabase.rpc('exec', {
sql_query: query
});
if (queryError && queryError.message && !queryError.message.includes('does not exist')) {
console.error(` ❌ Erro em query: ${queryError.message}`);
}
}
}
}
console.log(`${file} aplicada com sucesso!\n`);
} catch (err) {
console.error(` ❌ Erro ao aplicar ${file}:`);
console.error(` ${err.message}\n`);
}
}
console.log('4⃣ Verificando tabelas criadas...\n');
// Verificar tabelas
const { data: tables, error: tablesError } = await supabase
.from('information_schema.tables')
.select('table_name')
.eq('table_schema', 'public')
.order('table_name');
if (!tablesError && tables) {
console.log(`✅ Tabelas criadas (${tables.length}):\n`);
tables.forEach(t => console.log(`${t.table_name}`));
}
console.log(`
╔═══════════════════════════════════════════════════════════════╗
║ ║
║ ✅ MIGRATIONS APLICADAS COM SUCESSO! ║
║ ║
║ Próximo passo: npm run dev ║
║ ║
╚═══════════════════════════════════════════════════════════════╝
`);
} catch (error) {
console.error('❌ Erro ao aplicar migrations:', error.message);
process.exit(1);
}
}
deployMigrations();

460
scripts/file-watcher.ps1 Normal file
View File

@@ -0,0 +1,460 @@
# 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
}

View File

@@ -0,0 +1,575 @@
# Sistema de Logs e Notificações para RDO-C
# Gerencia logs detalhados e notificações em tempo real
# Uso: Import-Module .\scripts\logging-notifications.ps1
# Configurações globais de logging
$script:LogConfig = @{
LogDirectory = "logs"
MaxLogSize = 10MB
MaxLogFiles = 10
LogLevels = @("TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL")
DateFormat = "yyyy-MM-dd HH:mm:ss.fff"
EnableConsole = $true
EnableFile = $true
EnableNotifications = $true
NotificationTypes = @("desktop", "email", "webhook")
}
# Configurações de notificação
$script:NotificationConfig = @{
Desktop = @{
Enabled = $true
ShowSuccess = $false
ShowWarning = $true
ShowError = $true
Duration = 5000
}
Email = @{
Enabled = $false
SmtpServer = ""
Port = 587
Username = ""
Password = ""
From = ""
To = @()
Subject = "RDO-C Auto-Sync Notification"
}
Webhook = @{
Enabled = $false
Url = ""
Method = "POST"
Headers = @{}
AuthToken = ""
}
Slack = @{
Enabled = $false
WebhookUrl = ""
Channel = "#dev-notifications"
Username = "RDO-C Bot"
IconEmoji = ":robot_face:"
}
Discord = @{
Enabled = $false
WebhookUrl = ""
Username = "RDO-C Auto-Sync"
AvatarUrl = ""
}
}
# Cores para diferentes níveis de log
$script:LogColors = @{
TRACE = "DarkGray"
DEBUG = "Gray"
INFO = "White"
WARN = "Yellow"
ERROR = "Red"
FATAL = "Magenta"
SUCCESS = "Green"
}
# Emojis para notificações
$script:LogEmojis = @{
TRACE = "🔍"
DEBUG = "🐛"
INFO = ""
WARN = "⚠️"
ERROR = ""
FATAL = "💀"
SUCCESS = ""
SYNC = "🔄"
DEPLOY = "🚀"
GIT = "📝"
}
# Classe para gerenciar logs estruturados
class LogEntry {
[string]$Timestamp
[string]$Level
[string]$Category
[string]$Message
[hashtable]$Context
[string]$Source
[string]$Thread
[string]$SessionId
LogEntry([string]$level, [string]$message, [string]$category = "General", [hashtable]$context = @{}) {
$this.Timestamp = Get-Date -Format $script:LogConfig.DateFormat
$this.Level = $level.ToUpper()
$this.Category = $category
$this.Message = $message
$this.Context = $context
$this.Source = (Get-PSCallStack)[2].Command
$this.Thread = [System.Threading.Thread]::CurrentThread.ManagedThreadId
$this.SessionId = $env:RDO_SESSION_ID ?? (New-Guid).ToString().Substring(0,8)
}
[string] ToString() {
$contextStr = if ($this.Context.Count -gt 0) {
" | Context: $($this.Context | ConvertTo-Json -Compress)"
} else { "" }
return "[$($this.Timestamp)] [$($this.Level)] [$($this.Category)] [$($this.Source)] $($this.Message)$contextStr"
}
[hashtable] ToHashtable() {
return @{
timestamp = $this.Timestamp
level = $this.Level
category = $this.Category
message = $this.Message
context = $this.Context
source = $this.Source
thread = $this.Thread
session_id = $this.SessionId
}
}
}
# Inicializar sistema de logging
function Initialize-LoggingSystem {
param(
[string]$ConfigPath = "logging-config.json"
)
# Criar diretório de logs
if (-not (Test-Path $script:LogConfig.LogDirectory)) {
New-Item -ItemType Directory -Path $script:LogConfig.LogDirectory -Force | Out-Null
}
# Carregar configuração personalizada se existir
if (Test-Path $ConfigPath) {
try {
$customConfig = Get-Content $ConfigPath | ConvertFrom-Json
# Mesclar configurações
foreach ($key in $customConfig.PSObject.Properties.Name) {
if ($script:LogConfig.ContainsKey($key)) {
$script:LogConfig[$key] = $customConfig.$key
}
}
Write-LogEntry "INFO" "Configuração de logging carregada: $ConfigPath" "System"
} catch {
Write-LogEntry "WARN" "Falha ao carregar configuração: $($_.Exception.Message)" "System"
}
}
# Configurar ID da sessão
if (-not $env:RDO_SESSION_ID) {
$env:RDO_SESSION_ID = (New-Guid).ToString().Substring(0,8)
}
Write-LogEntry "INFO" "Sistema de logging inicializado - Sessão: $env:RDO_SESSION_ID" "System"
return $true
}
# Função principal de logging
function Write-LogEntry {
param(
[Parameter(Mandatory=$true)]
[ValidateSet("TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "SUCCESS")]
[string]$Level,
[Parameter(Mandatory=$true)]
[string]$Message,
[string]$Category = "General",
[hashtable]$Context = @{},
[string]$LogFile = "",
[switch]$NoConsole,
[switch]$NoFile,
[switch]$NoNotification
)
# Criar entrada de log
$logEntry = [LogEntry]::new($Level, $Message, $Category, $Context)
# Log no console
if ($script:LogConfig.EnableConsole -and -not $NoConsole) {
Write-ConsoleLog -LogEntry $logEntry
}
# Log em arquivo
if ($script:LogConfig.EnableFile -and -not $NoFile) {
Write-FileLog -LogEntry $logEntry -LogFile $LogFile
}
# Notificações
if ($script:LogConfig.EnableNotifications -and -not $NoNotification) {
Send-LogNotification -LogEntry $logEntry
}
return $logEntry
}
# Escrever log no console
function Write-ConsoleLog {
param([LogEntry]$LogEntry)
$color = $script:LogColors[$LogEntry.Level]
$emoji = $script:LogEmojis[$LogEntry.Level]
$prefix = "$emoji [$($LogEntry.Level)] [$($LogEntry.Category)]"
$message = "$prefix $($LogEntry.Message)"
Write-Host $message -ForegroundColor $color
# Mostrar contexto se disponível
if ($LogEntry.Context.Count -gt 0) {
$contextStr = $LogEntry.Context | ConvertTo-Json -Compress
Write-Host " Context: $contextStr" -ForegroundColor DarkGray
}
}
# Escrever log em arquivo
function Write-FileLog {
param(
[LogEntry]$LogEntry,
[string]$LogFile = ""
)
# Determinar arquivo de log
if (-not $LogFile) {
$date = Get-Date -Format "yyyy-MM-dd"
$LogFile = Join-Path $script:LogConfig.LogDirectory "rdo-auto-sync-$date.log"
} else {
$LogFile = Join-Path $script:LogConfig.LogDirectory $LogFile
}
try {
# Verificar rotação de logs
if (Test-Path $LogFile) {
$fileInfo = Get-Item $LogFile
if ($fileInfo.Length -gt $script:LogConfig.MaxLogSize) {
Rotate-LogFile -LogFile $LogFile
}
}
# Escrever entrada
$logLine = $LogEntry.ToString()
Add-Content -Path $LogFile -Value $logLine -Encoding UTF8
} catch {
Write-Host "❌ Erro ao escrever log: $($_.Exception.Message)" -ForegroundColor Red
}
}
# Rotacionar arquivos de log
function Rotate-LogFile {
param([string]$LogFile)
$directory = Split-Path $LogFile -Parent
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($LogFile)
$extension = [System.IO.Path]::GetExtension($LogFile)
# Mover arquivos existentes
for ($i = $script:LogConfig.MaxLogFiles - 1; $i -gt 0; $i--) {
$oldFile = Join-Path $directory "$baseName.$i$extension"
$newFile = Join-Path $directory "$baseName.$($i + 1)$extension"
if (Test-Path $oldFile) {
if ($i -eq ($script:LogConfig.MaxLogFiles - 1)) {
Remove-Item $oldFile -Force
} else {
Move-Item $oldFile $newFile -Force
}
}
}
# Mover arquivo atual
$rotatedFile = Join-Path $directory "$baseName.1$extension"
Move-Item $LogFile $rotatedFile -Force
Write-LogEntry "INFO" "Log rotacionado: $LogFile" "System"
}
# Enviar notificações
function Send-LogNotification {
param([LogEntry]$LogEntry)
# Filtrar por nível
$shouldNotify = switch ($LogEntry.Level) {
"SUCCESS" { $script:NotificationConfig.Desktop.ShowSuccess }
"WARN" { $script:NotificationConfig.Desktop.ShowWarning }
"ERROR" { $script:NotificationConfig.Desktop.ShowError }
"FATAL" { $true }
default { $false }
}
if (-not $shouldNotify) { return }
# Notificação desktop
if ($script:NotificationConfig.Desktop.Enabled) {
Send-DesktopNotification -LogEntry $LogEntry
}
# Notificação por email
if ($script:NotificationConfig.Email.Enabled) {
Send-EmailNotification -LogEntry $LogEntry
}
# Webhook genérico
if ($script:NotificationConfig.Webhook.Enabled) {
Send-WebhookNotification -LogEntry $LogEntry
}
# Slack
if ($script:NotificationConfig.Slack.Enabled) {
Send-SlackNotification -LogEntry $LogEntry
}
# Discord
if ($script:NotificationConfig.Discord.Enabled) {
Send-DiscordNotification -LogEntry $LogEntry
}
}
# Notificação desktop (Windows)
function Send-DesktopNotification {
param([LogEntry]$LogEntry)
try {
$emoji = $script:LogEmojis[$LogEntry.Level]
$title = "RDO-C Auto-Sync $emoji"
$message = "[$($LogEntry.Category)] $($LogEntry.Message)"
# Usar Windows Toast Notification
Add-Type -AssemblyName System.Windows.Forms
$notification = New-Object System.Windows.Forms.NotifyIcon
$notification.Icon = [System.Drawing.SystemIcons]::Information
$notification.BalloonTipTitle = $title
$notification.BalloonTipText = $message
$notification.Visible = $true
$notification.ShowBalloonTip($script:NotificationConfig.Desktop.Duration)
# Limpar após exibição
Start-Sleep -Milliseconds 100
$notification.Dispose()
} catch {
Write-Host "⚠️ Falha na notificação desktop: $($_.Exception.Message)" -ForegroundColor Yellow
}
}
# Notificação Slack
function Send-SlackNotification {
param([LogEntry]$LogEntry)
try {
$emoji = $script:LogEmojis[$LogEntry.Level]
$color = switch ($LogEntry.Level) {
"SUCCESS" { "good" }
"WARN" { "warning" }
"ERROR" { "danger" }
"FATAL" { "danger" }
default { "#36a64f" }
}
$payload = @{
channel = $script:NotificationConfig.Slack.Channel
username = $script:NotificationConfig.Slack.Username
icon_emoji = $script:NotificationConfig.Slack.IconEmoji
attachments = @(
@{
color = $color
title = "$emoji RDO-C Auto-Sync - $($LogEntry.Level)"
text = $LogEntry.Message
fields = @(
@{
title = "Categoria"
value = $LogEntry.Category
short = $true
},
@{
title = "Timestamp"
value = $LogEntry.Timestamp
short = $true
}
)
footer = "RDO-C Auto-Sync"
ts = [int][double]::Parse((Get-Date -UFormat %s))
}
)
}
$json = $payload | ConvertTo-Json -Depth 4
Invoke-RestMethod -Uri $script:NotificationConfig.Slack.WebhookUrl -Method POST -Body $json -ContentType "application/json"
} catch {
Write-Host "⚠️ Falha na notificação Slack: $($_.Exception.Message)" -ForegroundColor Yellow
}
}
# Notificação Discord
function Send-DiscordNotification {
param([LogEntry]$LogEntry)
try {
$emoji = $script:LogEmojis[$LogEntry.Level]
$color = switch ($LogEntry.Level) {
"SUCCESS" { 65280 } # Verde
"WARN" { 16776960 } # Amarelo
"ERROR" { 16711680 } # Vermelho
"FATAL" { 8388736 } # Roxo
default { 3447003 } # Azul
}
$payload = @{
username = $script:NotificationConfig.Discord.Username
avatar_url = $script:NotificationConfig.Discord.AvatarUrl
embeds = @(
@{
title = "$emoji RDO-C Auto-Sync - $($LogEntry.Level)"
description = $LogEntry.Message
color = $color
fields = @(
@{
name = "Categoria"
value = $LogEntry.Category
inline = $true
},
@{
name = "Fonte"
value = $LogEntry.Source
inline = $true
}
)
timestamp = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
footer = @{
text = "Sessão: $($LogEntry.SessionId)"
}
}
)
}
$json = $payload | ConvertTo-Json -Depth 4
Invoke-RestMethod -Uri $script:NotificationConfig.Discord.WebhookUrl -Method POST -Body $json -ContentType "application/json"
} catch {
Write-Host "⚠️ Falha na notificação Discord: $($_.Exception.Message)" -ForegroundColor Yellow
}
}
# Funções de conveniência para diferentes categorias
function Write-GitLog {
param([string]$Level, [string]$Message, [hashtable]$Context = @{})
Write-LogEntry -Level $Level -Message $Message -Category "Git" -Context $Context
}
function Write-SyncLog {
param([string]$Level, [string]$Message, [hashtable]$Context = @{})
Write-LogEntry -Level $Level -Message $Message -Category "Sync" -Context $Context
}
function Write-DeployLog {
param([string]$Level, [string]$Message, [hashtable]$Context = @{})
Write-LogEntry -Level $Level -Message $Message -Category "Deploy" -Context $Context
}
function Write-WatchLog {
param([string]$Level, [string]$Message, [hashtable]$Context = @{})
Write-LogEntry -Level $Level -Message $Message -Category "FileWatch" -Context $Context
}
# Análise de logs
function Get-LogAnalysis {
param(
[string]$LogFile = "",
[int]$LastHours = 24,
[string[]]$Levels = @(),
[string[]]$Categories = @()
)
if (-not $LogFile) {
$date = Get-Date -Format "yyyy-MM-dd"
$LogFile = Join-Path $script:LogConfig.LogDirectory "rdo-auto-sync-$date.log"
}
if (-not (Test-Path $LogFile)) {
Write-Host "❌ Arquivo de log não encontrado: $LogFile" -ForegroundColor Red
return
}
$cutoffTime = (Get-Date).AddHours(-$LastHours)
$logs = Get-Content $LogFile | ForEach-Object {
if ($_ -match '\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\] \[([A-Z]+)\] \[([^\]]+)\]') {
$timestamp = [DateTime]::ParseExact($matches[1], $script:LogConfig.DateFormat, $null)
if ($timestamp -gt $cutoffTime) {
@{
Timestamp = $timestamp
Level = $matches[2]
Category = $matches[3]
FullLine = $_
}
}
}
} | Where-Object { $_ -ne $null }
# Filtrar por níveis e categorias
if ($Levels.Count -gt 0) {
$logs = $logs | Where-Object { $_.Level -in $Levels }
}
if ($Categories.Count -gt 0) {
$logs = $logs | Where-Object { $_.Category -in $Categories }
}
# Estatísticas
$stats = @{
Total = $logs.Count
ByLevel = $logs | Group-Object Level | ForEach-Object { @{ $_.Name = $_.Count } }
ByCategory = $logs | Group-Object Category | ForEach-Object { @{ $_.Name = $_.Count } }
TimeRange = @{
Start = ($logs | Sort-Object Timestamp | Select-Object -First 1).Timestamp
End = ($logs | Sort-Object Timestamp | Select-Object -Last 1).Timestamp
}
}
Write-Host ""
Write-Host "📊 Análise de Logs - Últimas $LastHours horas" -ForegroundColor Cyan
Write-Host "============================================" -ForegroundColor Cyan
Write-Host "Total de entradas: $($stats.Total)" -ForegroundColor White
Write-Host ""
Write-Host "Por Nível:" -ForegroundColor Yellow
$stats.ByLevel | ForEach-Object {
$_.GetEnumerator() | ForEach-Object {
$emoji = $script:LogEmojis[$_.Key]
Write-Host " $emoji $($_.Key): $($_.Value)" -ForegroundColor White
}
}
Write-Host ""
Write-Host "Por Categoria:" -ForegroundColor Yellow
$stats.ByCategory | ForEach-Object {
$_.GetEnumerator() | ForEach-Object {
Write-Host " 📁 $($_.Key): $($_.Value)" -ForegroundColor White
}
}
return $stats
}
# Exportar funções
Export-ModuleMember -Function @(
'Initialize-LoggingSystem',
'Write-LogEntry',
'Write-GitLog',
'Write-SyncLog',
'Write-DeployLog',
'Write-WatchLog',
'Get-LogAnalysis',
'Send-LogNotification'
)
# Inicializar automaticamente se importado
if ($MyInvocation.InvocationName -eq 'Import-Module' -or $MyInvocation.InvocationName -eq '.') {
Initialize-LoggingSystem
}

102
scripts/setup-supabase.js Normal file
View File

@@ -0,0 +1,102 @@
#!/usr/bin/env node
import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import readline from 'readline';
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const question = (query) => new Promise(resolve => rl.question(query, resolve));
async function setupSupabase() {
console.log(`
╔═══════════════════════════════════════════════════════════════╗
║ ║
║ 🔗 CONFIGURAR CONEXÃO COM SUPABASE "RDO" ║
║ ║
╚═══════════════════════════════════════════════════════════════╝
`);
console.log('📋 Você vai precisar das credenciais do seu projeto Supabase "RDO"');
console.log(' Acesse: https://supabase.com/dashboard');
console.log(' Selecione o projeto "RDO"');
console.log(' Vá em: Settings → API\n');
// Obter credenciais
const supabaseUrl = await question('🔗 Project URL (https://...supabase.co): ');
const anonKey = await question('🔑 Anon Public Key: ');
// Validar
if (!supabaseUrl || !anonKey) {
console.error('\n❌ Erro: Credenciais não podem estar vazias!');
rl.close();
process.exit(1);
}
if (!supabaseUrl.includes('supabase.co')) {
console.error('\n❌ Erro: URL do Supabase inválida!');
rl.close();
process.exit(1);
}
if (!anonKey.startsWith('eyJ')) {
console.error('\n❌ Erro: Anon Key parece inválida (deve começar com "eyJ")!');
rl.close();
process.exit(1);
}
// Atualizar .env
console.log('\n⏳ Atualizando arquivo .env...');
const envPath = join(process.cwd(), '.env');
const envContent = `# Supabase Configuration
VITE_SUPABASE_URL=${supabaseUrl}
VITE_SUPABASE_ANON_KEY=${anonKey}
# Service Role Key (Backend Only - Never use in frontend)
# SUPABASE_SERVICE_ROLE_KEY=
`;
try {
writeFileSync(envPath, envContent);
console.log('✅ Arquivo .env atualizado com sucesso!\n');
} catch (error) {
console.error('❌ Erro ao atualizar .env:', error.message);
rl.close();
process.exit(1);
}
// Extrair project-ref
const projectRef = supabaseUrl.match(/https:\/\/([^.]+)\.supabase\.co/)?.[1];
console.log('📊 Resumo da configuração:');
console.log(` Project URL: ${supabaseUrl}`);
console.log(` Project Ref: ${projectRef}`);
console.log(` Anon Key: ${anonKey.substring(0, 20)}...`);
console.log('\n✅ Configuração concluída!\n');
console.log('📝 Próximos passos:\n');
console.log('1⃣ Linkar projeto Supabase CLI:');
console.log(` supabase link --project-ref ${projectRef}\n`);
console.log('2⃣ Verificar conexão:');
console.log(' node check-supabase-status.js\n');
console.log('3⃣ Aplicar migrations:');
console.log(' supabase db push\n');
console.log('4⃣ Iniciar desenvolvimento:');
console.log(' npm run dev\n');
rl.close();
}
setupSupabase().catch(error => {
console.error('❌ Erro:', error.message);
rl.close();
process.exit(1);
});

View File

@@ -0,0 +1,17 @@
@echo off
echo ===================================
echo Auto-Sync RDO v3.0
echo ===================================
echo.
echo Iniciando monitoramento automatico...
echo Repositorio: https://github.com/Reifonas/TS_RDO.git
echo Intervalo: 15 segundos
echo.
echo Pressione Ctrl+C para parar
echo.
node auto-sync.js
echo.
echo Auto-sync finalizado.
pause

476
scripts/start-auto-sync.ps1 Normal file
View File

@@ -0,0 +1,476 @@
# Script Principal - Sistema de Auto-Sync RDO-C
# Integra todos os componentes: sync, logs, notificações e monitoramento
# Uso: .\scripts\start-auto-sync.ps1 [opções]
param(
[ValidateSet("start", "stop", "restart", "status", "setup", "test")]
[string]$Action = "start",
[switch]$EnableNotifications,
[switch]$EnableFileWatch,
[switch]$Verbose,
[switch]$DryRun,
[switch]$Force,
[int]$SyncInterval = 300, # 5 minutos
[string]$ConfigFile = "auto-sync-config.json",
[string]$LogLevel = "INFO"
)
# Importar módulos necessários
$scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent
$rootDir = Split-Path $scriptDir -Parent
try {
Import-Module "$scriptDir\logging-notifications.ps1" -Force
Write-LogEntry "INFO" "Módulo de logging carregado com sucesso" "System"
} catch {
Write-Host "❌ Erro ao carregar módulo de logging: $($_.Exception.Message)" -ForegroundColor Red
exit 1
}
# Configurações globais
$script:AutoSyncConfig = @{
IsRunning = $false
StartTime = $null
ProcessId = $null
SyncCount = 0
ErrorCount = 0
LastSync = $null
StatusFile = "auto-sync-status.json"
PidFile = "auto-sync.pid"
}
# Verificar dependências
function Test-Dependencies {
Write-LogEntry "INFO" "Verificando dependências do sistema..." "System"
$dependencies = @(
@{ Name = "Git"; Command = "git --version" },
@{ Name = "Node.js"; Command = "node --version" },
@{ Name = "NPM"; Command = "npm --version" }
)
$missing = @()
foreach ($dep in $dependencies) {
try {
$result = Invoke-Expression $dep.Command 2>$null
if ($result) {
Write-LogEntry "SUCCESS" "$($dep.Name) encontrado: $($result.Split([Environment]::NewLine)[0])" "System"
} else {
$missing += $dep.Name
}
} catch {
$missing += $dep.Name
}
}
if ($missing.Count -gt 0) {
Write-LogEntry "ERROR" "Dependências não encontradas: $($missing -join ', ')" "System"
return $false
}
Write-LogEntry "SUCCESS" "Todas as dependências foram verificadas" "System"
return $true
}
# Configurar ambiente
function Initialize-Environment {
Write-LogEntry "INFO" "Inicializando ambiente de auto-sync..." "System"
# Verificar se é um repositório Git
if (-not (Test-Path ".git")) {
Write-LogEntry "ERROR" "Diretório atual não é um repositório Git" "Git"
return $false
}
# Verificar configuração do Git
try {
$gitUser = git config user.name
$gitEmail = git config user.email
if (-not $gitUser -or -not $gitEmail) {
Write-LogEntry "ERROR" "Configuração do Git incompleta (user.name ou user.email)" "Git"
return $false
}
Write-LogEntry "SUCCESS" "Git configurado para: $gitUser <$gitEmail>" "Git"
} catch {
Write-LogEntry "ERROR" "Erro ao verificar configuração do Git: $($_.Exception.Message)" "Git"
return $false
}
# Verificar conectividade com repositório remoto
try {
$remoteUrl = git config --get remote.origin.url
if ($remoteUrl) {
Write-LogEntry "SUCCESS" "Repositório remoto: $remoteUrl" "Git"
# Testar conectividade
git ls-remote origin HEAD 2>$null | Out-Null
if ($LASTEXITCODE -eq 0) {
Write-LogEntry "SUCCESS" "Conectividade com repositório remoto verificada" "Git"
} else {
Write-LogEntry "WARN" "Falha na conectividade com repositório remoto" "Git"
}
} else {
Write-LogEntry "WARN" "Nenhum repositório remoto configurado" "Git"
}
} catch {
Write-LogEntry "WARN" "Erro ao verificar repositório remoto: $($_.Exception.Message)" "Git"
}
# Criar diretórios necessários
$directories = @("logs", "temp", "scripts")
foreach ($dir in $directories) {
if (-not (Test-Path $dir)) {
New-Item -ItemType Directory -Path $dir -Force | Out-Null
Write-LogEntry "SUCCESS" "Diretório criado: $dir" "System"
}
}
# Configurar Git hooks se necessário
if (Test-Path "$scriptDir\setup-git-hooks.ps1") {
Write-LogEntry "INFO" "Configurando Git hooks..." "Git"
& "$scriptDir\setup-git-hooks.ps1"
}
return $true
}
# Salvar status do sistema
function Save-SystemStatus {
$status = @{
is_running = $script:AutoSyncConfig.IsRunning
start_time = $script:AutoSyncConfig.StartTime
process_id = $script:AutoSyncConfig.ProcessId
sync_count = $script:AutoSyncConfig.SyncCount
error_count = $script:AutoSyncConfig.ErrorCount
last_sync = $script:AutoSyncConfig.LastSync
last_update = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
config_file = $ConfigFile
log_level = $LogLevel
features = @{
notifications_enabled = $EnableNotifications
file_watch_enabled = $EnableFileWatch
}
}
try {
$status | ConvertTo-Json -Depth 3 | Set-Content $script:AutoSyncConfig.StatusFile
# Salvar PID se em execução
if ($script:AutoSyncConfig.IsRunning) {
$PID | Set-Content $script:AutoSyncConfig.PidFile
}
} catch {
Write-LogEntry "ERROR" "Erro ao salvar status: $($_.Exception.Message)" "System"
}
}
# Carregar status do sistema
function Get-SystemStatus {
if (Test-Path $script:AutoSyncConfig.StatusFile) {
try {
return Get-Content $script:AutoSyncConfig.StatusFile | ConvertFrom-Json
} catch {
Write-LogEntry "WARN" "Erro ao carregar status: $($_.Exception.Message)" "System"
}
}
return $null
}
# Executar sincronização
function Invoke-AutoSync {
Write-LogEntry "INFO" "Iniciando sincronização automática..." "Sync"
try {
# Verificar mudanças
$changes = git status --porcelain
if (-not $changes) {
Write-LogEntry "INFO" "Nenhuma mudança detectada" "Sync"
return $true
}
Write-LogEntry "INFO" "Mudanças detectadas: $($changes.Count) arquivos" "Sync" @{ files = $changes.Count }
if ($DryRun) {
Write-LogEntry "INFO" "[DRY RUN] Sincronização simulada" "Sync"
return $true
}
# Executar script de sync
if (Test-Path "$scriptDir\auto-sync-github.ps1") {
$syncResult = & "$scriptDir\auto-sync-github.ps1" -AutoCommit -Push
if ($syncResult) {
$script:AutoSyncConfig.SyncCount++
$script:AutoSyncConfig.LastSync = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-LogEntry "SUCCESS" "Sincronização concluída com sucesso" "Sync" @{ count = $script:AutoSyncConfig.SyncCount }
return $true
} else {
$script:AutoSyncConfig.ErrorCount++
Write-LogEntry "ERROR" "Falha na sincronização" "Sync"
return $false
}
} else {
Write-LogEntry "ERROR" "Script de sincronização não encontrado" "Sync"
return $false
}
} catch {
$script:AutoSyncConfig.ErrorCount++
Write-LogEntry "ERROR" "Erro durante sincronização: $($_.Exception.Message)" "Sync"
return $false
}
}
# Loop principal de monitoramento
function Start-AutoSyncLoop {
Write-LogEntry "SUCCESS" "Sistema de auto-sync iniciado" "System"
$script:AutoSyncConfig.IsRunning = $true
$script:AutoSyncConfig.StartTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$script:AutoSyncConfig.ProcessId = $PID
$lastSyncTime = Get-Date
# Iniciar file watcher se habilitado
$fileWatcherJob = $null
if ($EnableFileWatch -and (Test-Path "$scriptDir\file-watcher.ps1")) {
Write-LogEntry "INFO" "Iniciando monitoramento de arquivos..." "FileWatch"
$fileWatcherJob = Start-Job -ScriptBlock {
param($ScriptPath, $RootDir)
Set-Location $RootDir
& $ScriptPath -Continuous
} -ArgumentList "$scriptDir\file-watcher.ps1", $rootDir
}
try {
while ($script:AutoSyncConfig.IsRunning) {
$currentTime = Get-Date
# Salvar status periodicamente
Save-SystemStatus
# Verificar se é hora de sincronizar
if (($currentTime - $lastSyncTime).TotalSeconds -ge $SyncInterval) {
Invoke-AutoSync
$lastSyncTime = $currentTime
}
# Aguardar antes da próxima verificação
Start-Sleep -Seconds 30
# Verificar se deve parar (arquivo de controle)
if (Test-Path "auto-sync.stop") {
Write-LogEntry "INFO" "Arquivo de parada detectado" "System"
Remove-Item "auto-sync.stop" -Force
break
}
}
} catch {
Write-LogEntry "ERROR" "Erro no loop principal: $($_.Exception.Message)" "System"
} finally {
# Limpar recursos
if ($fileWatcherJob) {
Stop-Job $fileWatcherJob -Force
Remove-Job $fileWatcherJob -Force
}
$script:AutoSyncConfig.IsRunning = $false
Save-SystemStatus
# Remover arquivo PID
if (Test-Path $script:AutoSyncConfig.PidFile) {
Remove-Item $script:AutoSyncConfig.PidFile -Force
}
Write-LogEntry "INFO" "Sistema de auto-sync finalizado" "System"
}
}
# Parar sistema
function Stop-AutoSync {
Write-LogEntry "INFO" "Parando sistema de auto-sync..." "System"
# Criar arquivo de parada
"stop" | Set-Content "auto-sync.stop"
# Verificar se há processo em execução
if (Test-Path $script:AutoSyncConfig.PidFile) {
try {
$pid = Get-Content $script:AutoSyncConfig.PidFile
$process = Get-Process -Id $pid -ErrorAction SilentlyContinue
if ($process) {
Write-LogEntry "INFO" "Finalizando processo: $pid" "System"
Stop-Process -Id $pid -Force
Write-LogEntry "SUCCESS" "Processo finalizado" "System"
}
} catch {
Write-LogEntry "WARN" "Erro ao finalizar processo: $($_.Exception.Message)" "System"
}
}
$script:AutoSyncConfig.IsRunning = $false
Save-SystemStatus
}
# Mostrar status
function Show-SystemStatus {
$status = Get-SystemStatus
Write-Host ""
Write-Host "🔄 Status do Sistema Auto-Sync RDO-C" -ForegroundColor Cyan
Write-Host "===================================" -ForegroundColor Cyan
if ($status) {
$statusColor = if ($status.is_running) { "Green" } else { "Red" }
$statusText = if ($status.is_running) { "🟢 Executando" } else { "🔴 Parado" }
Write-Host "Status: $statusText" -ForegroundColor $statusColor
if ($status.start_time) {
Write-Host "Iniciado em: $($status.start_time)" -ForegroundColor Gray
}
if ($status.process_id) {
Write-Host "Process ID: $($status.process_id)" -ForegroundColor Gray
}
Write-Host ""
Write-Host "📊 Estatísticas:" -ForegroundColor Yellow
Write-Host " Sincronizações: $($status.sync_count)" -ForegroundColor White
Write-Host " Erros: $($status.error_count)" -ForegroundColor White
if ($status.last_sync) {
Write-Host " Última sincronização: $($status.last_sync)" -ForegroundColor White
}
Write-Host ""
Write-Host "⚙️ Configurações:" -ForegroundColor Yellow
Write-Host " Notificações habilitadas: $($status.features.notifications_enabled)" -ForegroundColor White
Write-Host " Monitoramento de arquivos: $($status.features.file_watch_enabled)" -ForegroundColor White
} else {
Write-Host "Status: 🔴 Não inicializado" -ForegroundColor Red
}
Write-Host ""
}
# Testar sistema
function Test-AutoSyncSystem {
Write-Host "🧪 Testando Sistema Auto-Sync" -ForegroundColor Cyan
Write-Host "=============================" -ForegroundColor Cyan
$tests = @(
@{ Name = "Dependências"; Test = { Test-Dependencies } },
@{ Name = "Ambiente Git"; Test = { Test-Path ".git" } },
@{ Name = "Scripts"; Test = {
(Test-Path "$scriptDir\auto-sync-github.ps1") -and
(Test-Path "$scriptDir\file-watcher.ps1")
}},
@{ Name = "Configuração"; Test = { Test-Path $ConfigFile } },
@{ Name = "Diretórios"; Test = {
(Test-Path "logs") -and (Test-Path "temp")
}}
)
$passed = 0
$total = $tests.Count
foreach ($test in $tests) {
try {
$result = & $test.Test
if ($result) {
Write-Host "$($test.Name)" -ForegroundColor Green
$passed++
} else {
Write-Host "$($test.Name)" -ForegroundColor Red
}
} catch {
Write-Host "$($test.Name): $($_.Exception.Message)" -ForegroundColor Red
}
}
Write-Host ""
Write-Host "Resultado: $passed/$total testes passaram" -ForegroundColor $(if ($passed -eq $total) { "Green" } else { "Yellow" })
return ($passed -eq $total)
}
# Função principal
function Main {
Write-Host "🚀 RDO-C Auto-Sync System" -ForegroundColor Cyan
Write-Host "=========================" -ForegroundColor Cyan
switch ($Action) {
"setup" {
Write-Host "⚙️ Configurando sistema..." -ForegroundColor Yellow
if (-not (Test-Dependencies)) {
Write-Host "❌ Falha na verificação de dependências" -ForegroundColor Red
exit 1
}
if (-not (Initialize-Environment)) {
Write-Host "❌ Falha na inicialização do ambiente" -ForegroundColor Red
exit 1
}
Write-Host "✅ Sistema configurado com sucesso" -ForegroundColor Green
}
"test" {
$success = Test-AutoSyncSystem
exit $(if ($success) { 0 } else { 1 })
}
"start" {
if (-not (Initialize-Environment)) {
Write-Host "❌ Falha na inicialização" -ForegroundColor Red
exit 1
}
Start-AutoSyncLoop
}
"stop" {
Stop-AutoSync
}
"restart" {
Stop-AutoSync
Start-Sleep -Seconds 3
if (Initialize-Environment) {
Start-AutoSyncLoop
}
}
"status" {
Show-SystemStatus
}
}
}
# Executar se chamado diretamente
if ($MyInvocation.InvocationName -ne '.') {
Main
}

32
scripts/start.js Normal file
View File

@@ -0,0 +1,32 @@
#!/usr/bin/env node
// Script de inicialização que funciona tanto localmente quanto em produção
import { spawn } from 'child_process';
const port = process.env.PORT || 4173;
console.log(`Starting preview server on port ${port}...`);
const child = spawn('npx', ['vite', 'preview', '--host', '0.0.0.0', '--port', port.toString()], {
stdio: 'inherit',
shell: true
});
child.on('error', (error) => {
console.error('Error starting server:', error);
process.exit(1);
});
child.on('exit', (code) => {
process.exit(code);
});
// Graceful shutdown
process.on('SIGINT', () => {
console.log('\nShutting down server...');
child.kill('SIGINT');
});
process.on('SIGTERM', () => {
console.log('\nShutting down server...');
child.kill('SIGTERM');
});

42
scripts/test-login.js Normal file
View File

@@ -0,0 +1,42 @@
// Script para testar o login com as credenciais criadas
import { createClient } from '@supabase/supabase-js';
// Configuração do Supabase
const supabaseUrl = 'https://bbyzrywmgjiufqtnkslu.supabase.co';
const supabaseKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJieXpyeXdtZ2ppdWZxdG5rc2x1Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTUxNjE4MzgsImV4cCI6MjA3MDczNzgzOH0.O0Eksg4_cxKk7jbC3wYLWbwZ9FsoTzsztnPPpzGL3pE';
const supabase = createClient(supabaseUrl, supabaseKey);
async function testLogin() {
console.log('🧪 Testando login com credenciais criadas...');
const email = 'teste@rdomain.com';
const password = 'teste123456';
try {
console.log(`📧 Email: ${email}`);
console.log(`🔒 Password: ${password}`);
const { data, error } = await supabase.auth.signInWithPassword({
email: email,
password: password
});
if (error) {
console.error('❌ Erro no login:', error.message);
console.error('📋 Detalhes do erro:', error);
} else {
console.log('✅ Login realizado com sucesso!');
console.log('👤 Usuário:', data.user?.email);
console.log('🎫 Session:', data.session ? 'Ativa' : 'Inativa');
// Fazer logout
await supabase.auth.signOut();
console.log('🚪 Logout realizado');
}
} catch (error) {
console.error('💥 Erro inesperado:', error);
}
}
testLogin();

View File

@@ -0,0 +1,44 @@
import { createClient } from '@supabase/supabase-js';
// Configuração do Supabase
const supabaseUrl = 'https://bbyzrywmgjiufqtnkslu.supabase.co';
const supabaseKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJieXpyeXdtZ2ppdWZxdG5rc2x1Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTUxNjE4MzgsImV4cCI6MjA3MDczNzgzOH0.O0Eksg4_cxKk7jbC3wYLWbwZ9FsoTzsztnPPpzGL3pE';
const supabase = createClient(supabaseUrl, supabaseKey);
async function createTestUser() {
console.log('🔄 Criando usuário de teste...');
const testEmail = 'teste@rdomain.com';
const testPassword = 'teste123456';
try {
// Tentar registrar o usuário
const { data, error } = await supabase.auth.signUp({
email: testEmail,
password: testPassword,
options: {
data: {
nome: 'Usuário Teste',
tipo: 'usuario'
}
}
});
if (error) {
console.error('❌ Erro ao criar usuário:', error.message);
return;
}
console.log('✅ Usuário criado com sucesso!');
console.log('📧 Email:', testEmail);
console.log('🔑 Senha:', testPassword);
console.log('👤 Dados:', data);
} catch (error) {
console.error('❌ Erro inesperado:', error);
}
}
// Executar a função
createTestUser();

47
scripts/test_buttons.js Normal file
View File

@@ -0,0 +1,47 @@
// Script para testar os botões do CreateRDO
// Execute este script no console do navegador na página CreateRDO
console.log('=== TESTE DOS BOTÕES DO RDO ===');
// Função para testar se um botão existe e está clicável
function testButton(selector, name) {
const button = document.querySelector(selector);
if (button) {
console.log(`✅ Botão "${name}" encontrado:`, button);
console.log(` - Texto: ${button.textContent.trim()}`);
console.log(` - Disabled: ${button.disabled}`);
console.log(` - Display: ${getComputedStyle(button).display}`);
console.log(` - Visibility: ${getComputedStyle(button).visibility}`);
// Testar clique
try {
button.click();
console.log(`✅ Clique no botão "${name}" executado com sucesso`);
} catch (error) {
console.error(`❌ Erro ao clicar no botão "${name}":`, error);
}
} else {
console.error(`❌ Botão "${name}" não encontrado com seletor: ${selector}`);
}
console.log('---');
}
// Testar botões específicos
testButton('button:contains("Adicionar Atividade")', 'Adicionar Atividade');
testButton('button:contains("Adicionar Inspeção de Solda")', 'Adicionar Inspeção de Solda');
testButton('button:contains("Adicionar Verificação de Torque")', 'Adicionar Verificação de Torque');
testButton('button:contains("Adicionar Mão de Obra")', 'Adicionar Mão de Obra');
// Alternativa: buscar por texto contendo
const buttons = Array.from(document.querySelectorAll('button'));
console.log('\n=== TODOS OS BOTÕES ENCONTRADOS ===');
buttons.forEach((btn, index) => {
const text = btn.textContent.trim();
if (text.includes('Adicionar')) {
console.log(`${index + 1}. "${text}"`);
console.log(` - Disabled: ${btn.disabled}`);
console.log(` - onClick: ${btn.onclick ? 'Definido' : 'Não definido'}`);
}
});
console.log('\n=== TESTE CONCLUÍDO ===');

View File

@@ -0,0 +1,89 @@
// Script de teste final para verificar funcionalidades do RDO
console.log('🧪 INICIANDO TESTE FINAL DE FUNCIONALIDADES');
// Função para aguardar um tempo
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// Função para testar se elementos existem
const testElementExists = (selector, description) => {
const element = document.querySelector(selector);
if (element) {
console.log(`${description}: ENCONTRADO`);
return element;
} else {
console.log(`${description}: NÃO ENCONTRADO`);
return null;
}
};
// Função para testar clique em botão
const testButtonClick = async (selector, description) => {
const button = testElementExists(selector, `Botão ${description}`);
if (button) {
console.log(`🎯 Testando clique no botão: ${description}`);
button.click();
await wait(500); // Aguarda meio segundo
console.log(`✅ Clique executado em: ${description}`);
return true;
}
return false;
};
// Função principal de teste
const runTests = async () => {
console.log('\n📋 VERIFICANDO ELEMENTOS DA PÁGINA...');
// Testar se a página carregou
testElementExists('h1', 'Título da página');
// Testar seções
testElementExists('[data-section="atividades"]', 'Seção de Atividades');
testElementExists('[data-section="inspecao"]', 'Seção de Inspeção de Qualidade');
testElementExists('[data-section="mao-obra"]', 'Seção de Mão de Obra');
console.log('\n🎯 TESTANDO BOTÕES DE ADICIONAR...');
// Testar botões de adicionar (usando texto do botão)
const buttons = [
{ text: 'Adicionar Atividade', description: 'Adicionar Atividade' },
{ text: 'Adicionar Inspeção de Solda', description: 'Adicionar Inspeção de Solda' },
{ text: 'Adicionar Verificação de Torque', description: 'Adicionar Verificação de Torque' }
];
for (const button of buttons) {
const buttonElement = Array.from(document.querySelectorAll('button'))
.find(btn => btn.textContent.includes(button.text));
if (buttonElement) {
console.log(`🎯 Testando: ${button.description}`);
buttonElement.click();
await wait(1000);
} else {
console.log(`❌ Botão não encontrado: ${button.description}`);
}
}
console.log('\n📊 VERIFICANDO DADOS CARREGADOS...');
// Verificar se há dados nas stores
if (window.useConfigStore) {
const store = window.useConfigStore.getState();
console.log('📋 Tipos de Atividade:', store.tiposAtividade?.length || 0);
console.log('🌤️ Condições Climáticas:', store.condicoesClimaticas?.length || 0);
} else {
console.log('⚠️ Store não encontrada no window');
}
console.log('\n✅ TESTE FINAL CONCLUÍDO!');
};
// Executar testes após carregamento da página
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', runTests);
} else {
runTests();
}
// Também executar após um delay para garantir que tudo carregou
setTimeout(runTests, 2000);

View File

@@ -0,0 +1,99 @@
import { createClient } from '@supabase/supabase-js'
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
// Read .env file
const envPath = path.resolve(__dirname, '../.env')
console.log('Lendo arquivo .env de:', envPath)
if (!fs.existsSync(envPath)) {
console.error('Arquivo .env não encontrado!')
process.exit(1)
}
const envContent = fs.readFileSync(envPath, 'utf-8')
const envConfig = {}
envContent.split('\n').forEach(line => {
const match = line.match(/^([^=]+)=(.*)$/)
if (match) {
const key = match[1].trim()
const value = match[2].trim().replace(/^"(.*)"$/, '$1').replace(/^'(.*)'$/, '$1')
envConfig[key] = value
}
})
const supabaseUrl = envConfig.VITE_SUPABASE_URL
const supabaseAnonKey = envConfig.VITE_SUPABASE_ANON_KEY
if (!supabaseUrl || !supabaseAnonKey) {
console.error('Credenciais Supabase não encontradas no .env')
console.log('Chaves encontradas:', Object.keys(envConfig))
process.exit(1)
}
const supabase = createClient(supabaseUrl, supabaseAnonKey)
const tables = [
'usuarios',
'obras',
'rdos',
'rdo_atividades',
'rdo_mao_obra',
'rdo_equipamentos',
'rdo_ocorrencias',
'rdo_anexos',
'rdo_inspecoes_solda',
'rdo_verificacoes_torque',
'tarefas',
'task_logs'
]
async function verify() {
console.log('Iniciando verificação de integridade do Supabase...')
console.log(`URL: ${supabaseUrl}`)
// Test connection first
const { data: authData, error: authError } = await supabase.auth.getSession()
if (authError) {
console.warn('Aviso: Erro ao verificar sessão de auth (pode ser normal se não houver sessão persistida):', authError.message)
}
let allGood = true
let summary = []
for (const table of tables) {
try {
// Using head: true to avoid fetching data, just checking access/existence
const { count, error } = await supabase
.from(table)
.select('*', { count: 'exact', head: true })
if (error) {
console.error(`❌ Tabela '${table}': Erro - ${error.message} (Code: ${error.code})`)
summary.push({ table, status: 'Erro', message: error.message })
allGood = false
} else {
console.log(`✅ Tabela '${table}': OK (Registros: ${count})`)
summary.push({ table, status: 'OK', count })
}
} catch (e) {
console.error(`❌ Tabela '${table}': Exceção - ${e.message}`)
summary.push({ table, status: 'Exceção', message: e.message })
allGood = false
}
}
console.log('\n--- Resumo ---')
if (allGood) {
console.log('Todas as tabelas verificadas parecem acessíveis.')
} else {
console.log('Foram encontrados problemas em algumas tabelas.')
}
}
verify()