first commit

This commit is contained in:
rodrigo committed 2025-04-10 23:07:21 -03:00
commit c9fb67e751
423 files changed
+53287

No files matched your search

+297
View File
@@ -0,0 +1,297 @@
import { prisma } from '../prismaClient';
import { createHash } from 'crypto';
import { PDFDocument, StandardFonts } from 'pdf-lib';
import sharp from 'sharp';
import * as fs from 'fs/promises';
import * as path from 'path';
import { execSync } from 'child_process';
import { BlockchainService } from './BlockchainService';
import { FileValidator } from './FileValidator';
import { ReportGenerator } from './ReportGenerator'; // Adicionar import
// Tentativa de importar o MetadataCollector com fallback
let MetadataCollectorClass;
try {
MetadataCollectorClass = require('./MetadataCollector').MetadataCollector;
} catch (error) {
console.warn('Aviso: MetadataCollector não está disponível, usando versão simplificada');
MetadataCollectorClass = require('./FallbackMetadataCollector').FallbackMetadataCollector;
}
export class ProvaService {
private fileValidator: FileValidator;
private reportGenerator: ReportGenerator;
private storageDir: string;
private metadataCollector: any;
constructor() {
this.fileValidator = new FileValidator();
this.reportGenerator = new ReportGenerator();
this.storageDir = path.join(process.cwd(), 'storage', 'provas');
// Inicializar o coletor de metadados com tratamento de erro
try {
this.metadataCollector = new MetadataCollectorClass();
} catch (error) {
console.error('Erro ao inicializar MetadataCollector, usando objeto vazio:', error);
this.metadataCollector = {
collectMetadata: async (data: any) => ({ ...data, error: 'Falha na coleta de metadados' })
};
}
this.ensureStorageDir();
}
private async ensureStorageDir() {
try {
await fs.mkdir(this.storageDir, { recursive: true });
} catch (error) {
console.error('Erro ao criar diretório de armazenamento:', error);
}
}
async processFile(file: Express.Multer.File, metadata: any) {
try {
// 1. Validar arquivo e metadados
const validationResult = await this.fileValidator.validateFile(file.buffer, file.mimetype);
if (!validationResult.valid) {
throw new Error(validationResult.error);
}
const metadataValidation = await this.fileValidator.validateMetadata(metadata);
if (!metadataValidation.valid) {
throw new Error(metadataValidation.error);
}
// 2. Coletar metadados avançados (com tratamento de erro)
let enhancedMetadata;
try {
enhancedMetadata = await this.metadataCollector.collectMetadata({
userAgent: metadata.userAgent,
ipAddress: metadata.ipAddress || '0.0.0.0',
timestamp: new Date(metadata.timestamp),
fileName: file.originalname,
fileModified: metadata.modification_times ? new Date(parseInt(metadata.modification_times)) : undefined,
fileSize: file.size,
viewportWidth: metadata.viewportWidth,
viewportHeight: metadata.viewportHeight,
language: metadata.language,
referrer: metadata.referrer,
latitude: metadata.latitude ? parseFloat(metadata.latitude) : undefined,
longitude: metadata.longitude ? parseFloat(metadata.longitude) : undefined
});
} catch (error) {
console.error('Erro ao coletar metadados avançados:', error);
enhancedMetadata = {
...metadata,
error: 'Falha na coleta de metadados avançados',
fallback: true
};
}
// 3. Gerar checksums
const checksums = this.generateChecksums(file.buffer);
// 4. Registrar timestamp
const timestamp = await this.createLocalTimestamp(file.buffer, checksums.sha256);
// 5. Processar arquivo
const processedFile = await this.processFileByType(file);
// 6. Gerar URL de verificação
const verificationUrl = `${process.env.BACKEND_URL || 'http://localhost:5000'}/verificar/${checksums.sha256}`;
// 7. Gerar relatório
const reportResult = await this.reportGenerator.generateReport({
file,
checksums,
timestamp,
metadata: enhancedMetadata,
processedFile,
verificationUrl
});
// 8. Salvar relatório
const reportPath = path.join(this.storageDir, `${checksums.sha256}_report.pdf`);
await fs.writeFile(reportPath, reportResult.buffer);
// 9. Salvar no banco de dados
const prova = await prisma.prova.create({
data: {
fileName: file.originalname,
mimeType: file.mimetype,
checksumSha256: checksums.sha256,
checksumSha512: checksums.sha512,
checksumMd5: checksums.md5,
metadata: enhancedMetadata,
reportPath: reportPath,
status: 'CONCLUIDO',
timestamp: JSON.stringify(timestamp)
}
});
// 10. Salvar o arquivo original
const originalFilePath = path.join(this.storageDir, `${checksums.sha256}_original`);
await fs.writeFile(originalFilePath, file.buffer);
// 11. Registrar log forense
await this.logForensicInfo(file, enhancedMetadata, checksums);
return prova;
} catch (error) {
console.error('Erro ao processar arquivo:', error);
throw error;
} finally {
// Limpar recursos
await this.fileValidator.cleanup();
}
}
private generateChecksums(buffer: Buffer) {
return {
sha256: createHash('sha256').update(buffer).digest('hex'),
sha512: createHash('sha512').update(buffer).digest('hex'),
md5: createHash('md5').update(buffer).digest('hex')
};
}
private async createLocalTimestamp(buffer: Buffer, hash: string) {
try {
console.log('Criando timestamp local...');
// 1. Tempo atual em formato RFC3339
const now = new Date();
const isoString = now.toISOString();
// 2. Obter tempo NTP do servidor oficial para maior confiabilidade
let ntpTime = '';
try {
// Usar servidores NTP confiáveis para obter tempo preciso
ntpTime = execSync('w32tm /stripchart /computer:time.nist.gov /samples:1 /dataonly').toString();
} catch (e) {
console.warn('Não foi possível obter tempo NTP, usando tempo local:', e);
}
// 3. Criar arquivo de timestamp com metadados e assinatura
const timestampData = {
hash,
timestamp: isoString,
ntpReference: ntpTime || 'Indisponível',
unixTimestamp: Math.floor(now.getTime() / 1000)
};
// 4. Salvar o arquivo de timestamp
const timestampPath = path.join(this.storageDir, `${hash}.timestamp.json`);
await fs.writeFile(timestampPath, JSON.stringify(timestampData, null, 2));
return {
...timestampData,
path: timestampPath
};
} catch (error) {
console.error('Erro ao criar timestamp local:', error);
throw new Error(`Falha ao criar timestamp: ${error.message}`);
}
}
private async processFileByType(file: Express.Multer.File) {
if (file.mimetype.startsWith('image/')) {
try {
return await sharp(file.buffer)
.resize(1200, 900, {
fit: 'inside',
withoutEnlargement: true
})
.toBuffer();
} catch (error) {
console.error('Erro ao processar imagem com sharp:', error);
return file.buffer; // Fallback para o buffer original
}
}
return file.buffer;
}
async verifyFile(hash: string) {
try {
// 1. Buscar prova no banco de dados
const prova = await prisma.prova.findFirst({
where: { checksumSha256: hash }
});
if (!prova) {
return { verified: false, message: 'Arquivo não encontrado na base de dados' };
}
// 2. Verificar se o arquivo original existe
const originalFilePath = path.join(this.storageDir, `${hash}_original`);
try {
await fs.access(originalFilePath);
} catch (error) {
return {
verified: false,
message: 'Arquivo original não encontrado no servidor',
prova
};
}
// 3. Verificar se o timestamp existe
const timestampPath = path.join(this.storageDir, `${hash}.timestamp.json`);
try {
await fs.access(timestampPath);
} catch (error) {
return {
verified: false,
message: 'Timestamp não encontrado',
prova
};
}
// 4. Ler o timestamp
const timestampData = JSON.parse(await fs.readFile(timestampPath, 'utf-8'));
return {
verified: true,
message: 'Arquivo verificado com sucesso',
prova,
timestamp: timestampData
};
} catch (error) {
console.error('Erro ao verificar arquivo:', error);
return { verified: false, message: `Erro ao verificar: ${error.message}` };
}
}
// Novo método para registrar informações forenses
private async logForensicInfo(file: Express.Multer.File, metadata: any, checksums: any) {
try {
const logEntry = {
timestamp: new Date().toISOString(),
fileName: file.originalname,
fileSize: file.size,
mimeType: file.mimetype,
checksums,
metadata,
processingInfo: {
processingNode: require('os').hostname(),
processingUser: process.env.USER || process.env.USERNAME || 'unknown',
processingTimestamp: new Date().toISOString(),
systemInfo: {
platform: process.platform,
release: require('os').release(),
cpus: require('os').cpus().length,
memory: Math.round(require('os').totalmem() / (1024 * 1024 * 1024)) + 'GB',
uptime: Math.round(require('os').uptime() / 3600) + 'h',
}
}
};
const logPath = path.join(this.storageDir, `${checksums.sha256}_forensic.json`);
await fs.writeFile(logPath, JSON.stringify(logEntry, null, 2));
console.log('Log forense registrado em:', logPath);
} catch (error) {
console.error('Erro ao registrar log forense:', error);
// Não interromper o processamento por falha no log
}
}
}