Fix IP in ProvaService.ts and Add PUBLIC_URL
Signed-off-by: Rodrigo Martins <rodrigo.sousa.577@gmail.com>
This commit is contained in:
1 parent
8dfe0d6f94
commit
839f931b75
3 files changed
+211
-5
No files matched your search
@@ -14,6 +14,11 @@ dotenv.config();
|
|||||||
const app = express();
|
const app = express();
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
// Configurar para confiar em proxies, permitindo a leitura de X-Forwarded-For
|
||||||
|
// Isso é necessário se sua aplicação estiver atrás de um balanceador de carga,
|
||||||
|
// proxy reverso ou Cloudflare
|
||||||
|
app.set('trust proxy', true);
|
||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
|
||||||
|
export const ipDetectionMiddleware = (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
// Extrair o endereço IP real
|
||||||
|
req.realIP = determineRealIP(req);
|
||||||
|
|
||||||
|
// Registrar a detecção para debug
|
||||||
|
console.log(`Requisição de ${req.realIP} (original: ${req.ip})`);
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
function determineRealIP(req: Request): string {
|
||||||
|
// Array de possíveis fontes de IP, em ordem de preferência
|
||||||
|
const ipSources = [
|
||||||
|
req.headers['x-forwarded-for'],
|
||||||
|
req.headers['x-real-ip'],
|
||||||
|
req.headers['cf-connecting-ip'],
|
||||||
|
req.ip,
|
||||||
|
req.socket?.remoteAddress
|
||||||
|
];
|
||||||
|
|
||||||
|
// Filtrar valores nulos e processar x-forwarded-for que pode conter múltiplos IPs
|
||||||
|
for (const source of ipSources) {
|
||||||
|
if (source) {
|
||||||
|
// x-forwarded-for pode ter múltiplos IPs separados por vírgula
|
||||||
|
if (typeof source === 'string' && source.includes(',')) {
|
||||||
|
const firstIP = source.split(',')[0].trim();
|
||||||
|
if (firstIP && isValidIP(firstIP)) return firstIP;
|
||||||
|
} else if (isValidIP(source as string)) {
|
||||||
|
return source as string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback para um valor padrão se nenhum IP válido for encontrado
|
||||||
|
return '0.0.0.0';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidIP(ip: string): boolean {
|
||||||
|
// Função simples para verificar se uma string parece um IP válido
|
||||||
|
return !!ip && ip !== 'unknown' && ip !== '::1' && ip !== 'localhost';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estender a interface Request para incluir a propriedade realIP
|
||||||
|
declare global {
|
||||||
|
namespace Express {
|
||||||
|
interface Request {
|
||||||
|
realIP: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,8 +6,10 @@ import * as fs from 'fs/promises';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
import { BlockchainService } from './BlockchainService';
|
import { BlockchainService } from './BlockchainService';
|
||||||
|
import { ipDetectionMiddleware } from '../middleware/ipDetection';
|
||||||
import { FileValidator } from './FileValidator';
|
import { FileValidator } from './FileValidator';
|
||||||
import { ReportGenerator } from './ReportGenerator'; // Adicionar import
|
import { ReportGenerator } from './ReportGenerator'; // Adicionar import
|
||||||
|
import { Request, Response } from 'express'; // Importar Request e Response
|
||||||
|
|
||||||
// Tentativa de importar o MetadataCollector com fallback
|
// Tentativa de importar o MetadataCollector com fallback
|
||||||
let MetadataCollectorClass;
|
let MetadataCollectorClass;
|
||||||
@@ -66,9 +68,12 @@ export class ProvaService {
|
|||||||
// 2. Coletar metadados avançados (com tratamento de erro)
|
// 2. Coletar metadados avançados (com tratamento de erro)
|
||||||
let enhancedMetadata;
|
let enhancedMetadata;
|
||||||
try {
|
try {
|
||||||
|
// Garantir que estamos usando o IP real do cliente
|
||||||
|
const clientIP = this.extractRealClientIP(metadata.ipAddress, metadata);
|
||||||
|
|
||||||
enhancedMetadata = await this.metadataCollector.collectMetadata({
|
enhancedMetadata = await this.metadataCollector.collectMetadata({
|
||||||
userAgent: metadata.userAgent,
|
userAgent: metadata.userAgent,
|
||||||
ipAddress: metadata.ipAddress || '0.0.0.0',
|
ipAddress: clientIP,
|
||||||
timestamp: new Date(metadata.timestamp),
|
timestamp: new Date(metadata.timestamp),
|
||||||
fileName: file.originalname,
|
fileName: file.originalname,
|
||||||
fileModified: metadata.modification_times ? new Date(parseInt(metadata.modification_times)) : undefined,
|
fileModified: metadata.modification_times ? new Date(parseInt(metadata.modification_times)) : undefined,
|
||||||
@@ -99,7 +104,7 @@ export class ProvaService {
|
|||||||
const processedFile = await this.processFileByType(file);
|
const processedFile = await this.processFileByType(file);
|
||||||
|
|
||||||
// 6. Gerar URL de verificação
|
// 6. Gerar URL de verificação
|
||||||
const verificationUrl = `${process.env.BACKEND_URL || 'http://localhost:5000'}/verificar/${checksums.sha256}`;
|
const verificationUrl = `${process.env.PUBLIC_URL || 'http://localhost:5000'}/verificar/${checksums.sha256}`;
|
||||||
|
|
||||||
// 7. Gerar relatório
|
// 7. Gerar relatório
|
||||||
const reportResult = await this.reportGenerator.generateReport({
|
const reportResult = await this.reportGenerator.generateReport({
|
||||||
@@ -147,6 +152,94 @@ export class ProvaService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extrai o endereço IP real do cliente, ignorando IPs locais quando possível
|
||||||
|
* @param ipAddress O endereço IP fornecido na requisição
|
||||||
|
* @param metadata Metadados adicionais para análise
|
||||||
|
* @returns O endereço IP real do cliente
|
||||||
|
*/
|
||||||
|
private extractRealClientIP(ipAddress: string, metadata?: any): string {
|
||||||
|
// Verificar se temos dados válidos para trabalhar
|
||||||
|
if (!metadata) {
|
||||||
|
return ipAddress || '0.0.0.0';
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Tentando extrair IP real. IP original:', ipAddress);
|
||||||
|
|
||||||
|
// Lista de IPs locais/internos que devem ser evitados
|
||||||
|
const localIPs = ['::1', '127.0.0.1', 'localhost', '::ffff:127.0.0.1', '::ffff:10.15.0.1'];
|
||||||
|
|
||||||
|
// Verificar primeiro se temos o IP encaminhado nas informações de conexão
|
||||||
|
if (metadata.connectionInfo && metadata.connectionInfo.forwardedIp) {
|
||||||
|
const forwardedIp = metadata.connectionInfo.forwardedIp;
|
||||||
|
if (!localIPs.includes(forwardedIp)) {
|
||||||
|
console.log('Usando forwardedIp dos dados de conexão:', forwardedIp);
|
||||||
|
return forwardedIp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar se temos o IP do cliente nos headers HTTP
|
||||||
|
if (metadata.headers) {
|
||||||
|
// Headers comuns que podem conter o IP real
|
||||||
|
const headerCandidates = [
|
||||||
|
'x-forwarded-for',
|
||||||
|
'X-Forwarded-For',
|
||||||
|
'x-real-ip',
|
||||||
|
'X-Real-IP',
|
||||||
|
'cf-connecting-ip',
|
||||||
|
'CF-Connecting-IP',
|
||||||
|
'true-client-ip',
|
||||||
|
'True-Client-IP'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Tentar cada header candidato
|
||||||
|
for (const header of headerCandidates) {
|
||||||
|
if (metadata.headers[header]) {
|
||||||
|
const headerValue = metadata.headers[header];
|
||||||
|
// X-Forwarded-For pode conter múltiplos IPs separados por vírgula
|
||||||
|
if (header.toLowerCase() === 'x-forwarded-for') {
|
||||||
|
const ips = headerValue.split(',').map(ip => ip.trim());
|
||||||
|
if (ips.length > 0 && !localIPs.includes(ips[0])) {
|
||||||
|
console.log(`Usando IP do header ${header}:`, ips[0]);
|
||||||
|
return ips[0];
|
||||||
|
}
|
||||||
|
} else if (!localIPs.includes(headerValue)) {
|
||||||
|
console.log(`Usando IP do header ${header}:`, headerValue);
|
||||||
|
return headerValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tentar extrair o IP das informações de conexão direta
|
||||||
|
if (metadata.remoteAddress && !localIPs.includes(metadata.remoteAddress)) {
|
||||||
|
console.log('Usando remoteAddress:', metadata.remoteAddress);
|
||||||
|
return metadata.remoteAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se temos um valor em clientPort, usar para identificação do cliente
|
||||||
|
if (metadata.clientPort) {
|
||||||
|
const clientPort = metadata.clientPort;
|
||||||
|
console.log('Usando porta do cliente para identificação:', clientPort);
|
||||||
|
return `${ipAddress}:${clientPort}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se o IP é local, tentar criar um identificador único
|
||||||
|
if (localIPs.includes(ipAddress)) {
|
||||||
|
if (metadata.userAgent) {
|
||||||
|
// Adicionar um hash identificador baseado no userAgent
|
||||||
|
const userAgentHash = createHash('md5').update(metadata.userAgent).digest('hex').substring(0, 8);
|
||||||
|
const ipUserAgent = `${ipAddress}#${userAgentHash}`;
|
||||||
|
console.log('Usando combinação IP#userAgentHash:', ipUserAgent);
|
||||||
|
return ipUserAgent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se chegamos aqui, não encontramos nenhuma alternativa melhor
|
||||||
|
console.log('Nenhum IP alternativo encontrado, usando IP original:', ipAddress || '0.0.0.0');
|
||||||
|
return ipAddress || '0.0.0.0';
|
||||||
|
}
|
||||||
|
|
||||||
private generateChecksums(buffer: Buffer) {
|
private generateChecksums(buffer: Buffer) {
|
||||||
return {
|
return {
|
||||||
sha256: createHash('sha256').update(buffer).digest('hex'),
|
sha256: createHash('sha256').update(buffer).digest('hex'),
|
||||||
@@ -166,8 +259,15 @@ export class ProvaService {
|
|||||||
// 2. Obter tempo NTP do servidor oficial para maior confiabilidade
|
// 2. Obter tempo NTP do servidor oficial para maior confiabilidade
|
||||||
let ntpTime = '';
|
let ntpTime = '';
|
||||||
try {
|
try {
|
||||||
// Usar servidores NTP confiáveis para obter tempo preciso
|
// Detectar plataforma e usar o comando apropriado
|
||||||
ntpTime = execSync('w32tm /stripchart /computer:time.nist.gov /samples:1 /dataonly').toString();
|
if (process.platform === 'win32') {
|
||||||
|
// Windows
|
||||||
|
ntpTime = execSync('w32tm /stripchart /computer:time.nist.gov /samples:1 /dataonly').toString();
|
||||||
|
} else {
|
||||||
|
// Linux/Unix/MacOS - usar implementação alternativa
|
||||||
|
// Tentar usar o comando 'date' para obter a hora do sistema
|
||||||
|
ntpTime = execSync('date -u +"%Y-%m-%dT%H:%M:%S.%3NZ"').toString().trim();
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Não foi possível obter tempo NTP, usando tempo local:', e);
|
console.warn('Não foi possível obter tempo NTP, usando tempo local:', e);
|
||||||
}
|
}
|
||||||
@@ -190,7 +290,16 @@ export class ProvaService {
|
|||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erro ao criar timestamp local:', error);
|
console.error('Erro ao criar timestamp local:', error);
|
||||||
throw new Error(`Falha ao criar timestamp: ${error.message}`);
|
// Em caso de erro, retornar um timestamp básico sem interromper o processo
|
||||||
|
const basicTimestamp = {
|
||||||
|
hash,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
ntpReference: 'Indisponível (erro)',
|
||||||
|
unixTimestamp: Math.floor(Date.now() / 1000),
|
||||||
|
error: error.message
|
||||||
|
};
|
||||||
|
|
||||||
|
return basicTimestamp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,3 +404,43 @@ export class ProvaService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No handler de upload de arquivo:
|
||||||
|
export const uploadProva = async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const file = req.file;
|
||||||
|
if (!file) {
|
||||||
|
return res.status(400).json({ error: 'Nenhum arquivo enviado' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Coletar todas as informações possíveis sobre o cliente
|
||||||
|
const metadata = {
|
||||||
|
ipAddress: req.ip, // Express geralmente fornece isso
|
||||||
|
remoteAddress: req.socket?.remoteAddress, // Endereço IP direto do socket
|
||||||
|
porta: req.socket?.remotePort?.toString(), // Porta de conexão do cliente
|
||||||
|
userAgent: req.headers['user-agent'],
|
||||||
|
headers: req.headers, // Todos os headers HTTP
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
// Outros metadados do formulário
|
||||||
|
...req.body
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('Metadados de conexão:', {
|
||||||
|
ip: req.ip,
|
||||||
|
remoteAddress: req.socket?.remoteAddress,
|
||||||
|
remotePort: req.socket?.remotePort,
|
||||||
|
headers: {
|
||||||
|
'x-forwarded-for': req.headers['x-forwarded-for'],
|
||||||
|
'x-real-ip': req.headers['x-real-ip']
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const provaService = new ProvaService();
|
||||||
|
const result = await provaService.processFile(file, metadata);
|
||||||
|
|
||||||
|
return res.status(200).json(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erro ao processar upload de prova:', error);
|
||||||
|
return res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in new issue
Block a user