first commit
This commit is contained in:
commit
c9fb67e751
423 files changed
+53287
No files matched your search
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user