First commit - backup RDOC
This commit is contained in:
53
scripts/apply-migrations-cli.sh
Normal file
53
scripts/apply-migrations-cli.sh
Normal 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
102
scripts/apply-migrations.js
Normal 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');
|
||||
48
scripts/apply-migrations.ps1
Normal file
48
scripts/apply-migrations.ps1
Normal 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"
|
||||
246
scripts/auto-sync-github.ps1
Normal file
246
scripts/auto-sync-github.ps1
Normal 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
358
scripts/auto-sync.js
Normal 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
178
scripts/auto-syncRDO.ps1
Normal 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
|
||||
110
scripts/check-supabase-status.js
Normal file
110
scripts/check-supabase-status.js
Normal 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
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
57
scripts/check_database.js
Normal 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();
|
||||
68
scripts/confirm-user-email.js
Normal file
68
scripts/confirm-user-email.js
Normal 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();
|
||||
51
scripts/create-confirmed-user.js
Normal file
51
scripts/create-confirmed-user.js
Normal 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();
|
||||
147
scripts/deploy-migrations.js
Normal file
147
scripts/deploy-migrations.js
Normal 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
460
scripts/file-watcher.ps1
Normal 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
|
||||
}
|
||||
575
scripts/logging-notifications.ps1
Normal file
575
scripts/logging-notifications.ps1
Normal 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
102
scripts/setup-supabase.js
Normal 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);
|
||||
});
|
||||
17
scripts/start-auto-sync.bat
Normal file
17
scripts/start-auto-sync.bat
Normal 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
476
scripts/start-auto-sync.ps1
Normal 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
32
scripts/start.js
Normal 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
42
scripts/test-login.js
Normal 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();
|
||||
44
scripts/test-user-creation.js
Normal file
44
scripts/test-user-creation.js
Normal 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
47
scripts/test_buttons.js
Normal 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 ===');
|
||||
89
scripts/test_final_functionality.js
Normal file
89
scripts/test_final_functionality.js
Normal 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);
|
||||
99
scripts/verify-supabase.js
Normal file
99
scripts/verify-supabase.js
Normal 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()
|
||||
Reference in New Issue
Block a user